name: Docker Release-v2

on:
  workflow_dispatch:
    inputs:
      TAG_NAME:
        description: 'Tag name that the major tag will point to'
        required: true
      PRERELEASE:
        description: 'Whether this is a prerelease'
        type: boolean
        required: true

  release:
    types: [published]

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

env:
  TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}
  IS_PRERELEASE: ${{ github.event.release.prerelease || github.event.inputs.PRERELEASE }}
  IMAGE: ghcr.io/dragonflydb/dragonfly
  GCS_IMAGE: us-central1-docker.pkg.dev/dragonflydb-public/dragonfly-registry/dragonfly

jobs:
  build_and_tag:
    name: Build and Push ${{matrix.flavor}} ${{ matrix.os.arch }} image
    strategy:
      matrix:
        flavor: [ubuntu]
        os:
          - image: ubuntu-24.04
            arch: amd64
          - image: ubuntu-24.04-arm
            arch: arm64

    runs-on: ${{ matrix.os.image }}
    permissions:
      contents: read
      packages: write
      id-token: write

    steps:
      - name: checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          submodules: true
      - name: Set up Docker Build
        uses: docker/setup-buildx-action@v3

      - name: Login to Registries
        uses: ./.github/actions/multi-registry-docker-login
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}

      - name: Fetch release asset
        uses: dsaltares/fetch-gh-release-asset@1.1.2
        with:
          version: "tags/${{ env.TAG_NAME }}"
          regex: true
          file: "dragonfly-.*\\.tar\\.gz"
          target: 'releases/'
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract artifacts
        run: |
          echo "Event prerelease ${{ github.event.release.prerelease }}"
          echo "Input prerelease ${{ github.event.inputs.PRERELEASE }}"
          ls -l
          ls -l releases
          for f in releases/*.tar.gz; do tar xvfz $f -C releases; done
          rm releases/*.tar.gz

      - name: Docker meta
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: |
            ${{ env.IMAGE }}
            ${{ env.GCS_IMAGE }}
          flavor: |
            latest=false
            prefix=${{ matrix.flavor}}-
            suffix=-${{ matrix.os.arch }}
          tags: |
            type=semver,pattern={{version}},enable=true,value=${{ env.TAG_NAME }}
            type=semver,pattern={{raw}},enable=true,value=${{ env.TAG_NAME }}
            type=ref,event=pr
          labels: |
            org.opencontainers.image.vendor=DragonflyDB LTD
            org.opencontainers.image.title=Dragonfly Production Image
            org.opencontainers.image.description=The fastest in-memory store
            org.opencontainers.image.version=${{ env.TAG_NAME }}

      - name: Build image
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          provenance: false  # Prevent pushing a docker manifest
          tags: |
            ${{ steps.metadata.outputs.tags }}
          labels: ${{ steps.metadata.outputs.labels }}
          file: tools/packaging/Dockerfile.${{ matrix.flavor }}-prod
          cache-from: type=gha,scope=prod-${{ matrix.flavor }}
          cache-to: type=gha,scope=prod-${{ matrix.flavor }},mode=max
          load: true  # Load the build images into the local docker.

      - name: Test Image
        uses: ./.github/actions/test-docker
        timeout-minutes: 1
        with:
          image_id: ${{ env.IMAGE }}@${{ steps.build.outputs.digest }}
          name: ${{ matrix.flavor }}-${{ matrix.os.arch }}

      - id: output-sha
        run: |
          echo "sha_${{ matrix.os.arch }}=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT
    outputs:
      sha_amd: ${{ steps.output-sha.outputs.sha_amd64 }}
      sha_arm: ${{ steps.output-sha.outputs.sha_arm64 }}

  merge_manifest:
    needs: [build_and_tag]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        flavor: [ubuntu]
    steps:
      - name: checkout
        uses: actions/checkout@v4

      - name: Login to Registries
        uses: ./.github/actions/multi-registry-docker-login
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}

      - name: Merge and Push
        run: |
            # Function to create and push manifests for a given registry
            create_and_push_manifests() {
              local registry=$1
              local sha_amd=$2
              local sha_arm=$3
              local flavor=$4
              local tag_name=$5
              local is_prerelease=$6

              # Function for semantic version comparison
              # Returns true if current_version >= latest_version
              semver_cmp() {
                local current_version=$1
                local latest_version=$2
                local should_update=true

                # Extract major.minor.patch components
                IFS='.' read -ra CURRENT_PARTS <<< "$current_version"
                IFS='.' read -ra LATEST_PARTS <<< "$latest_version"

                # Pad arrays to same length for comparison
                while [ ${#CURRENT_PARTS[@]} -lt 3 ]; do CURRENT_PARTS+=(0); done
                while [ ${#LATEST_PARTS[@]} -lt 3 ]; do LATEST_PARTS+=(0); done

                # Compare major.minor.patch numerically
                if (( 10#${CURRENT_PARTS[0]} < 10#${LATEST_PARTS[0]} )); then
                  should_update=false
                elif (( 10#${CURRENT_PARTS[0]} == 10#${LATEST_PARTS[0]} )) && (( 10#${CURRENT_PARTS[1]} < 10#${LATEST_PARTS[1]} )); then
                  should_update=false
                elif (( 10#${CURRENT_PARTS[0]} == 10#${LATEST_PARTS[0]} )) && (( 10#${CURRENT_PARTS[1]} == 10#${LATEST_PARTS[1]} )) && (( 10#${CURRENT_PARTS[2]} < 10#${LATEST_PARTS[2]} )); then
                  should_update=false
                fi

                # Log debug info to stderr instead of stdout
                echo "Version comparison: current=${CURRENT_PARTS[0]}.${CURRENT_PARTS[1]}.${CURRENT_PARTS[2]} vs latest=${LATEST_PARTS[0]}.${LATEST_PARTS[1]}.${LATEST_PARTS[2]}" >&2

                # Return only the result
                echo $should_update
              }

              if [[ "$is_prerelease" == 'true' ]]; then
                # Create and push the manifest like dragonfly:alpha-ubuntu
                tag="${registry}:alpha-${flavor}"
                docker manifest create ${tag} --amend ${sha_amd} --amend ${sha_arm}
                docker manifest push ${tag}
              elif [[ "$flavor" == 'ubuntu' ]]; then
                # Checking if this version should be tagged as latest
                echo "Checking if ${tag_name} should be tagged as latest..."

                # Remove 'v' prefix if present for semantic comparison
                current_version=${tag_name#v}

                # Get the current latest version by running the latest image
                latest_version=""
                if docker pull ${registry}:latest &>/dev/null; then
                  echo "Found latest tag, checking its version..."

                  # First try to get version from image labels using docker inspect
                  echo "Method 1: Trying to get version from image labels..."
                  label_version=$(docker image inspect --format '{{ index .Config.Labels "org.opencontainers.image.version" }}' ${registry}:latest 2>/dev/null || echo "")

                  if [[ -n "$label_version" ]]; then
                    echo "Found version from image labels: $label_version"

                    # Extract version from format like "ubuntu-1.28.1-arm64"
                    if [[ $label_version == ubuntu-*-* ]]; then
                      # Extract the middle part (version) from ubuntu-VERSION-arch
                      latest_full_version=$(echo "$label_version" | cut -d'-' -f2)
                    else
                      # Use the label as is
                      latest_full_version=$label_version
                    fi

                    echo "Extracted version: $latest_full_version"
                  else
                    # Fallback to running the container if label inspect failed
                    echo "Method 2: Falling back to container execution..."
                    latest_full_version=$(docker run --rm --entrypoint /bin/sh ${registry}:latest -c "dragonfly --version | cut -d' ' -f2 | head -n 1")
                  fi

                  echo "Latest full version: ${latest_full_version}"

                  # Extract only the semantic version part (before any dash)
                  latest_version=$(echo "${latest_full_version}" | cut -d'-' -f1)
                  # Remove 'v' prefix if present
                  latest_version=${latest_version#v}
                  echo "Current latest version: ${latest_version}"
                else
                  echo "No latest tag found yet or couldn't pull it"
                fi

                # Compare versions only if we have a latest version
                should_update_latest=true
                if [[ -n "$latest_version" ]]; then
                  # Call our semver comparison function
                  should_update_latest=$(semver_cmp "$current_version" "$latest_version")
                fi

                if [[ "$should_update_latest" == true ]]; then
                  echo "Version ${tag_name} is newer than or equal to current latest, updating latest tag"
                  tag="${registry}:latest"
                  # Create and push the manifest like dragonfly:latest
                  docker manifest create ${tag} --amend ${sha_amd} --amend ${sha_arm}
                  docker manifest push ${tag}
                else
                  echo "Version ${tag_name} is older than current latest (${latest_version}), NOT updating latest tag"
                fi
              fi

              # Create and push the manifest like dragonfly:v1.26.4
              tag="${registry}:${tag_name}"
              docker manifest create ${tag} --amend ${sha_amd} --amend ${sha_arm}
              docker manifest push ${tag}
            }

            # GitHub Container Registry manifests
            ghcr_sha_amd=${{ env.IMAGE }}@${{ needs.build_and_tag.outputs.sha_amd }}
            ghcr_sha_arm=${{ env.IMAGE }}@${{ needs.build_and_tag.outputs.sha_arm }}
            create_and_push_manifests "${{ env.IMAGE }}" "$ghcr_sha_amd" "$ghcr_sha_arm" "${{ matrix.flavor }}" "${{ env.TAG_NAME }}" "${{ env.IS_PRERELEASE }}"

            # Google Artifact Registry manifests
            gar_sha_amd=${{ env.GCS_IMAGE }}@${{ needs.build_and_tag.outputs.sha_amd }}
            gar_sha_arm=${{ env.GCS_IMAGE }}@${{ needs.build_and_tag.outputs.sha_arm }}
            create_and_push_manifests "${{ env.GCS_IMAGE }}" "$gar_sha_amd" "$gar_sha_arm" "${{ matrix.flavor }}" "${{ env.TAG_NAME }}" "${{ env.IS_PRERELEASE }}"

  release_helm_and_notify:
    needs: [merge_manifest]
    runs-on: ubuntu-latest
    steps:
    - name: print_env
      run: env

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

    - name: Install helm
      uses: azure/setup-helm@v4

    - name: Setup Go
      uses: actions/setup-go@v5

    - name: Configure Git
      if: env.IS_PRERELEASE != 'true'
      run: |
        git config user.name "$GITHUB_ACTOR"
        git config user.email "$GITHUB_ACTOR@users.noreply.github.com"

    - name: Update helm chart
      if: env.IS_PRERELEASE != 'true'
      run: |
        git checkout main
        sed -Ei \
            -e 's/^(version\:) .*/\1 '${{ env.TAG_NAME }}'/g' \
            -e 's/^(appVersion\:) .*/\1 "'${{ env.TAG_NAME }}'"/g' \
            contrib/charts/dragonfly/Chart.yaml

        go test ./contrib/charts/dragonfly/... -update

        git commit \
          -m 'chore(helm-chart): update to ${{ env.TAG_NAME }}' \
          contrib/charts/dragonfly/Chart.yaml \
          contrib/charts/dragonfly/ci || true

    - name: Push Helm chart as OCI to Github
      if: env.IS_PRERELEASE != 'true'
      run: |
        echo "${{ secrets.GITHUB_TOKEN }}" | \
          helm registry login -u ${{ github.actor }} --password-stdin ghcr.io

        helm package contrib/charts/dragonfly

        helm push dragonfly-${{ env.TAG_NAME }}.tgz oci://ghcr.io/${{ github.repository }}/helm

    - name: GitHub Push
      uses: CasperWA/push-protected@v2
      if: env.IS_PRERELEASE != 'true'
      with:
        token: ${{ secrets.DRAGONFLY_TOKEN }}
        branch: main

    - name: Discord notification
      env:
        DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
      uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9
      with:
        args: 'DragonflyDB version [${{ env.TAG_NAME }}](https://github.com/dragonflydb/dragonfly/releases/tag/${{ env.TAG_NAME }}) has been released 🎉'

    - name: Re-build Docs
      if: env.IS_PRERELEASE != 'true'
      run: |
        curl -s -X POST '${{ secrets.VERCEL_DOCS_WEBHOOK }}'
