name: Build and Push API & Web

on:
  push:
    branches:
      - "main"
      - "deploy/**"
      - "build/**"
      - "release/e-*"
      - "hotfix/**"
      - "feat/hitl-backend"
    tags:
      - "*"

concurrency:
  group: build-push-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

env:
  DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
  DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
  DIFY_WEB_IMAGE_NAME: ${{ vars.DIFY_WEB_IMAGE_NAME || 'langgenius/dify-web' }}
  DIFY_API_IMAGE_NAME: ${{ vars.DIFY_API_IMAGE_NAME || 'langgenius/dify-api' }}

jobs:
  build:
    runs-on: ${{ matrix.runs_on }}
    if: github.repository == 'langgenius/dify'
    permissions:
      contents: read
      id-token: write
    strategy:
      matrix:
        include:
          - service_name: "build-api-amd64"
            image_name_env: "DIFY_API_IMAGE_NAME"
            artifact_context: "api"
            build_context: "{{defaultContext}}"
            file: "api/Dockerfile"
            platform: linux/amd64
            runs_on: depot-ubuntu-24.04-4
          - service_name: "build-api-arm64"
            image_name_env: "DIFY_API_IMAGE_NAME"
            artifact_context: "api"
            build_context: "{{defaultContext}}"
            file: "api/Dockerfile"
            platform: linux/arm64
            runs_on: depot-ubuntu-24.04-4
          - service_name: "build-web-amd64"
            image_name_env: "DIFY_WEB_IMAGE_NAME"
            artifact_context: "web"
            build_context: "{{defaultContext}}"
            file: "web/Dockerfile"
            platform: linux/amd64
            runs_on: depot-ubuntu-24.04-4
          - service_name: "build-web-arm64"
            image_name_env: "DIFY_WEB_IMAGE_NAME"
            artifact_context: "web"
            build_context: "{{defaultContext}}"
            file: "web/Dockerfile"
            platform: linux/arm64
            runs_on: depot-ubuntu-24.04-4

    steps:
      - name: Prepare
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV

      - name: Login to Docker Hub
        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
        with:
          username: ${{ env.DOCKERHUB_USER }}
          password: ${{ env.DOCKERHUB_TOKEN }}

      - name: Set up Depot CLI
        uses: depot/setup-action@15c09a5f77a0840ad4bce955686522a257853461 # v1.7.1

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: ${{ env[matrix.image_name_env] }}

      - name: Build Docker image
        id: build
        uses: depot/build-push-action@5f3b3c2e5a00f0093de47f657aeaefcedff27d18 # v1.17.0
        with:
          project: ${{ vars.DEPOT_PROJECT_ID }}
          context: ${{ matrix.build_context }}
          file: ${{ matrix.file }}
          platforms: ${{ matrix.platform }}
          build-args: COMMIT_SHA=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
          labels: ${{ steps.meta.outputs.labels }}
          outputs: type=image,name=${{ env[matrix.image_name_env] }},push-by-digest=true,name-canonical=true,push=true

      - name: Export digest
        env:
          DIGEST: ${{ steps.build.outputs.digest }}
        run: |
          mkdir -p /tmp/digests
          sanitized_digest=${DIGEST#sha256:}
          touch "/tmp/digests/${sanitized_digest}"

      - name: Upload digest
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: digests-${{ matrix.artifact_context }}-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  fork-build-validate:
    if: github.repository != 'langgenius/dify'
    runs-on: ubuntu-24.04
    strategy:
      matrix:
        include:
          - service_name: "validate-api-amd64"
            build_context: "{{defaultContext}}"
            file: "api/Dockerfile"
          - service_name: "validate-web-amd64"
            build_context: "{{defaultContext}}"
            file: "web/Dockerfile"
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0

      - name: Validate Docker image
        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0
        with:
          push: false
          context: ${{ matrix.build_context }}
          file: ${{ matrix.file }}
          platforms: linux/amd64

  create-manifest:
    needs: build
    runs-on: depot-ubuntu-24.04
    if: github.repository == 'langgenius/dify'
    strategy:
      matrix:
        include:
          - service_name: "merge-api-images"
            image_name_env: "DIFY_API_IMAGE_NAME"
            context: "api"
          - service_name: "merge-web-images"
            image_name_env: "DIFY_WEB_IMAGE_NAME"
            context: "web"
    steps:
      - name: Download digests
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: /tmp/digests
          pattern: digests-${{ matrix.context }}-*
          merge-multiple: true

      - name: Login to Docker Hub
        uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
        with:
          username: ${{ env.DOCKERHUB_USER }}
          password: ${{ env.DOCKERHUB_TOKEN }}

      - name: Extract metadata for Docker
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: ${{ env[matrix.image_name_env] }}
          tags: |
            type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-') }}
            type=ref,event=branch
            type=sha,enable=true,priority=100,prefix=,suffix=,format=long
            type=raw,value=${{ github.ref_name }},enable=${{ startsWith(github.ref, 'refs/tags/') }}

      - name: Create manifest list and push
        working-directory: /tmp/digests
        env:
          IMAGE_NAME: ${{ env[matrix.image_name_env] }}
        run: |
          docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            $(printf "$IMAGE_NAME@sha256:%s " *)

      - name: Inspect image
        env:
          IMAGE_NAME: ${{ env[matrix.image_name_env] }}
          IMAGE_VERSION: ${{ steps.meta.outputs.version }}
        run: |
          docker buildx imagetools inspect "$IMAGE_NAME:$IMAGE_VERSION"
