name: Docker

# thanks to https://github.com/sredevopsorg/multi-arch-docker-github-workflow
# This workflow builds and publishes to GHCR (GitHub Container Registry)
# For Docker Hub releases, see release.yml

on:
  push:
    branches: ["main"]
    tags: ["v*.*.*"]
  pull_request:
    branches: ["main"]
  workflow_dispatch:

env:
  # GitHub Container Registry image name
  GHCR_IMAGE: ghcr.io/${{ github.repository }}
  # Docker Hub image name (for merge job)
  DOCKER_IMAGE: convertx/convertx-cn

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # The build job builds the Docker image for each platform specified in the matrix.
  build:
    strategy:
      fail-fast: false
      matrix:
        platform:
          - linux/amd64
          - linux/arm64

    permissions:
      contents: write
      packages: write
      attestations: write
      id-token: write

    runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-24.04' || matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' }}

    name: Build Docker image for ${{ matrix.platform }}

    steps:
      - name: Prepare environment for current platform
        # This step sets up the environment for the current platform being built.
        # It replaces the '/' character in the platform name with '-' and sets it as an environment variable.
        # This is useful for naming artifacts and other resources that cannot contain '/'.
        # The environment variable PLATFORMS_PAIR will be used later in the workflow.
        id: prepare
        run: |
          platform=${{ matrix.platform }}
          echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV

      - name: Checkout repository
        uses: actions/checkout@v4

      # ========================================
      # 清理磁碟空間（解決 no space left on device）
      # ========================================
      # 🔥 GitHub Actions Runner 只有約 14GB 可用空間
      #    大型模型 build 需要清理更多預裝軟體
      # ========================================
      - name: Free disk space (aggressive)
        run: |
          echo "🧹 Aggressive disk cleanup for large model builds..."
          echo "========================================"
          echo "📊 初始磁碟空間："
          df -h /
          echo "========================================"

          # ========================================
          # 移除大型預裝軟體（約可釋放 30-40GB）
          # ========================================
          echo "🗑️ 移除預裝開發工具..."

          # .NET SDK（約 2-3GB）
          sudo rm -rf /usr/share/dotnet || true

          # Android SDK（約 10-15GB）- 最大宗！
          sudo rm -rf /usr/local/lib/android || true

          # Haskell/GHC（約 1GB）
          sudo rm -rf /opt/ghc || true

          # CodeQL（約 1GB）
          sudo rm -rf /opt/hostedtoolcache/CodeQL || true

          # Boost（約 500MB）
          sudo rm -rf /usr/local/share/boost || true

          # Swift（約 1.5GB）
          sudo rm -rf /usr/share/swift || true

          # Hosted Tool Cache（約 5-8GB）
          sudo rm -rf /opt/hostedtoolcache || true

          # Azure CLI（約 500MB）
          sudo rm -rf /usr/share/az_* || true
          sudo rm -rf /opt/az || true

          # Google Cloud SDK（約 500MB）
          sudo rm -rf /usr/lib/google-cloud-sdk || true

          # PowerShell（約 200MB）
          sudo rm -rf /usr/local/share/powershell || true

          # Miniconda（約 500MB）
          sudo rm -rf /usr/share/miniconda || true

          # ========================================
          # 清理系統 cache
          # ========================================
          echo "🗑️ 清理系統 cache..."

          # apt cache
          sudo apt-get clean || true
          sudo apt-get autoremove -y || true
          sudo rm -rf /var/lib/apt/lists/* || true

          # npm/yarn global cache
          sudo rm -rf /usr/local/share/.cache || true

          # ========================================
          # 清理 Docker 舊資料
          # ========================================
          echo "🗑️ 清理 Docker cache..."
          docker system prune -af --volumes || true

          # ========================================
          # 清理 BuildKit cache（關鍵！解決 overlayfs 爆空間）
          # ========================================
          echo "🗑️ 清理 BuildKit cache..."
          # BuildKit 的 blob 和 layer cache 可能佔用大量空間
          sudo rm -rf /var/lib/docker/buildkit || true

          echo "========================================"
          echo "📊 清理後磁碟空間："
          df -h /
          echo "========================================"

      - name: downcase REPO
        run: |
          echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"

      - name: Docker meta default
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ env.REPO }}

      # ========================================
      # 設定 Docker Buildx（針對大型模型優化）
      # ========================================
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          platforms: ${{ matrix.platform }}
          # 🔥 使用 docker-container driver 以獲得更好的 cache 控制
          driver-opts: |
            image=moby/buildkit:latest
            network=host

      - name: Login to GitHub Container Registry
        # here we only login to ghcr.io since the this only pushes internal images
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # ========================================
      # Build and Push（針對大型模型優化）
      # ========================================
      # 🔥 注意事項：
      #   - 使用 registry cache 而非 GHA cache（避免 10GB 限制）
      #   - 啟用 compression 減少傳輸量
      #   - 使用 inline cache 確保 layer 可重用
      # ========================================
      - name: Build and push by digest
        id: build
        uses: docker/build-push-action@v6
        env:
          DOCKER_BUILDKIT: 1
        with:
          context: .
          platforms: ${{ matrix.platform }}
          labels: ${{ steps.meta.outputs.labels }}
          annotations: ${{ steps.meta.outputs.annotations }}
          outputs: type=image,name=ghcr.io/${{ env.REPO }},push-by-digest=true,name-canonical=true,oci-mediatypes=true,compression=zstd,compression-level=3
          push: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)) }}
          # 🔥 使用 registry cache 取代 GHA cache
          # registry cache 沒有 10GB 限制，更適合大型模型
          cache-from: type=registry,ref=ghcr.io/${{ env.REPO }}:buildcache-${{ env.PLATFORM_PAIR }}
          cache-to: type=registry,ref=ghcr.io/${{ env.REPO }}:buildcache-${{ env.PLATFORM_PAIR }},mode=max,compression=zstd
          # 設定 build 參數
          build-args: |
            BUILDKIT_INLINE_CACHE=1

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

      - name: Upload digest
        uses: actions/upload-artifact@v6
        with:
          name: digests-${{ env.PLATFORM_PAIR }}
          path: /tmp/digests/*
          if-no-files-found: error
          retention-days: 1

  merge:
    if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)) }}
    name: Merge Docker manifests
    runs-on: ubuntu-latest

    permissions:
      contents: write
      packages: write
      attestations: write
      id-token: write

    needs:
      - build
    steps:
      - name: Download digests
        uses: actions/download-artifact@v7
        with:
          path: /tmp/digests
          pattern: digests-*
          merge-multiple: true

      - name: downcase REPO
        run: |
          echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            ghcr.io/${{ env.REPO }}
            ${{ env.DOCKER_IMAGE }}

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Get execution timestamp with RFC3339 format
        id: timestamp
        run: |
          echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT

      - name: Create manifest list and push
        working-directory: /tmp/digests
        run: |
          docker buildx imagetools create \
            $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
            --annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \
            --annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \
            --annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \
            --annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \
            $(printf 'ghcr.io/${{ env.REPO }}@sha256:%s ' *)

      - name: Inspect image
        run: |
          docker buildx imagetools inspect 'ghcr.io/${{ env.REPO }}:${{ steps.meta.outputs.version }}'
