# ============================================================
# Docker Build & Push（Remote Host via Tailscale SSH）
# ============================================================
# 說明：使用 Tailscale SSH 連線到私有主機執行 Docker build
# 重要：此 workflow 完全不使用傳統 SSH key，僅使用 Tailscale SSH
# ============================================================

name: Docker Build & Push (Remote Host via Tailscale)

on:
  # 手動觸發，支援輸入 image tag
  workflow_dispatch:
    inputs:
      image_tag:
        description: "Docker Image Tag（例如：0.1.12.test）"
        required: true
        default: "0.1.12.test"
        type: string

# ============================================================
# 環境變數設定
# ============================================================
env:
  # 遠端主機工作目錄
  REMOTE_WORKDIR: /home/bioailab/miniconda3/lid/app/docker_build
  # 專案名稱
  PROJECT_NAME: ConvertX-CN
  # Docker Hub 映像庫
  DOCKER_IMAGE_REPO: convertx/convertx-cn

# ============================================================
# 權限設定（GITHUB_TOKEN）
# ============================================================
permissions:
  contents: write # 必要：創建 Release、上傳 assets、創建/推送 tag
  actions: read # 可選：讀取 workflow 資訊（用於 generate release notes）

jobs:
  build-and-push:
    name: 🚀 遠端 Docker Build & Push
    runs-on: ubuntu-latest

    steps:
      # ========================================
      # Step 1: Checkout repository
      # 說明：檢出程式碼（雖然 build 在遠端，但需要 workflow 完整性）
      # ========================================
      - name: 📥 Checkout repository
        uses: actions/checkout@v4

      # ========================================
      # Step 2: 啟動 Tailscale（非互動模式）
      # 說明：使用 Tailscale Auth Key 加入 tailnet，啟用 SSH
      # 重要：必須使用 --ssh 參數才能使用 tailscale ssh
      # ========================================
      - name: 🔗 啟動 Tailscale 連線（非互動模式）
        uses: tailscale/github-action@v2
        with:
          authkey: ${{ secrets.TAILSCALE_AUTHKEY }}
          args: "--ssh --accept-dns=false --accept-routes=false"
          # 使用 ephemeral key，runner 結束後自動離開 tailnet
          # --ssh: 啟用 Tailscale SSH 功能
          # --accept-dns=false: 不使用 Tailscale DNS
          # --accept-routes=false: 不接受其他節點的路由

      # ========================================
      # Step 3: 驗證 Tailscale 連線
      # 說明：確認 Tailscale 已成功連線並顯示狀態
      # ========================================
      - name: ✅ 驗證 Tailscale 連線狀態
        run: |
          echo "========================================"
          echo "🔍 檢查 Tailscale 連線狀態"
          echo "========================================"

          # 顯示 Tailscale 狀態
          echo ""
          echo "📊 Tailscale 狀態："
          tailscale status

          # 顯示本機 Tailscale IP
          echo ""
          echo "🌐 本機 Tailscale IP："
          TAILSCALE_IP=$(tailscale ip -4)
          echo "${TAILSCALE_IP}"

          # 確認 SSH 功能已啟用
          echo ""
          echo "🔐 檢查 SSH 功能狀態："
          if tailscale status --json | grep -q '"SSH":true'; then
            echo "✅ Tailscale SSH 功能已啟用"
          else
            echo "⚠️ 正在檢查 SSH 狀態..."
            tailscale status --json | head -50
          fi

          # 等待 Tailscale 完全連線
          echo ""
          echo "⏳ 等待 Tailscale 連線穩定..."
          sleep 5

          # 測試與目標主機的連通性
          echo ""
          echo "📡 測試與遠端主機的連通性..."
          echo "目標主機: ${{ secrets.SSH_HOST }}"

          if tailscale ping --c 3 "${{ secrets.SSH_HOST }}"; then
            echo ""
            echo "✅ Tailscale 連線測試成功！"
          else
            echo ""
            echo "⚠️ Tailscale ping 失敗，但 SSH 可能仍可運作，繼續執行..."
          fi

          echo "========================================"

      # ========================================
      # Step 4: Tailscale SSH 連線（手動網頁驗證）
      # 說明：首次連線需要點擊驗證 URL 完成授權
      # ========================================
      - name: 🔐 測試 Tailscale SSH 連線
        run: |
          echo "========================================"
          echo "🔐 測試 Tailscale SSH 連線"
          echo "========================================"
          echo "使用者: ${{ secrets.SSH_USER }}"
          echo "主機: ${{ secrets.SSH_HOST }}"
          echo ""
          echo "⚠️  如果出現驗證 URL，請在瀏覽器中開啟並完成驗證"
          echo "⏳ 驗證完成後，連線會自動繼續..."
          echo ""
          echo "========================================"

          # 執行 SSH 連線，輸出會包含驗證連結
          # 使用 timeout 避免無限等待（10 分鐘）
          timeout 600 tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "echo '✅ Tailscale SSH 連線成功！' && echo '主機名稱:' && hostname && echo '當前使用者:' && whoami" \
            || { echo ""; echo "❌ SSH 連線失敗或超時，請確認已完成網頁驗證"; exit 1; }

          echo ""
          echo "========================================"

      # ========================================
      # Step 5: Smoke Test（環境驗證）
      # 說明：快速驗證遠端環境是否正常
      # ========================================
      - name: 🧪 Smoke Test（環境驗證）
        run: |
          echo "========================================"
          echo "🧪 Smoke Test - 驗證遠端環境"
          echo "========================================"

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'REMOTE_EOF'
          set -e

          echo "📋 測試項目 1：進入目標目錄"
          echo "----------------------------------------"
          WORKDIR="/home/bioailab/miniconda3/lid/app/docker_build"
          mkdir -p "${WORKDIR}"
          cd "${WORKDIR}"
          echo "✅ 目前目錄: $(pwd)"

          echo ""
          echo "📋 測試項目 2：Git 版本"
          echo "----------------------------------------"
          git --version
          echo "✅ Git 可用"

          echo ""
          echo "📋 測試項目 3：Docker 版本"
          echo "----------------------------------------"
          docker version --format '{{.Server.Version}}' || docker --version
          echo "✅ Docker 可用"

          echo ""
          echo "📋 測試項目 4：Docker Buildx 版本"
          echo "----------------------------------------"
          docker buildx version
          echo "✅ Docker Buildx 可用"

          echo ""
          echo "========================================"
          echo "✅ 所有 Smoke Test 通過！"
          echo "========================================"
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 6: 準備遠端工作目錄
      # 說明：確保遠端工作目錄存在
      # ========================================
      - name: 📁 準備遠端工作目錄
        run: |
          echo "========================================"
          echo "📁 準備遠端工作目錄"
          echo "========================================"
          echo "工作目錄: ${{ env.REMOTE_WORKDIR }}"
          echo ""

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "mkdir -p ${{ env.REMOTE_WORKDIR }} && echo '✅ 工作目錄已就緒'"

          echo "========================================"

      # ========================================
      # Step 7: Clone 或更新專案程式碼
      # 說明：在遠端主機上 clone 或 pull 最新程式碼
      # ========================================
      - name: 📥 Clone 或更新專案程式碼
        run: |
          echo "========================================"
          echo "📥 Clone 或更新專案程式碼"
          echo "========================================"

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'REMOTE_EOF'
          set -e

          WORKDIR="${{ env.REMOTE_WORKDIR }}"
          PROJECT="${{ env.PROJECT_NAME }}"
          REPO_URL="https://github.com/pi-docket/ConvertX-CN.git"

          echo "📂 進入工作目錄: ${WORKDIR}"
          cd "${WORKDIR}"

          if [ -d "${PROJECT}" ]; then
            echo "📦 專案已存在，執行 git pull..."
            cd "${PROJECT}"
            git fetch origin
            git reset --hard origin/main
            git pull origin main
            echo "✅ 程式碼更新完成"
          else
            echo "📦 專案不存在，執行 git clone..."
            git clone "${REPO_URL}" "${PROJECT}"
            cd "${PROJECT}"
            echo "✅ 專案 clone 完成"
          fi

          echo ""
          echo "📋 目前 commit："
          git log -1 --oneline

          echo ""
          echo "✅ 程式碼準備完成"
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 8: Docker Login
      # 說明：在遠端主機登入 Docker Hub
      # ========================================
      - name: 🔐 Docker Hub 登入
        run: |
          echo "========================================"
          echo "🔐 Docker Hub 登入"
          echo "========================================"

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "echo '${{ secrets.DOCKERHUB_TOKEN }}' | docker login -u '${{ secrets.DOCKERHUB_USERNAME }}' --password-stdin && echo '✅ Docker Hub 登入成功'"

          echo "========================================"

      # ========================================
      # Step 9: 設定 Docker Buildx（Multi-Arch 必要）
      # 說明：建立支援多架構的 buildx builder
      # ========================================
      - name: 🔧 設定 Docker Buildx
        run: |
          echo "========================================"
          echo "🔧 設定 Docker Buildx（Multi-Arch 支援）"
          echo "========================================"

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'REMOTE_EOF'
          set -e

          BUILDER_NAME="multiarch-builder-${{ github.run_id }}"

          echo "📋 檢查現有 buildx builders..."
          docker buildx ls

          echo ""
          echo "🔧 建立新的 multi-arch builder: ${BUILDER_NAME}"
          docker buildx create --name "${BUILDER_NAME}" --driver docker-container --use --bootstrap

          echo ""
          echo "✅ Buildx builder 已就緒"
          echo ""
          echo "📋 目前 builder 狀態："
          docker buildx inspect --bootstrap

          echo ""
          echo "🏗️ 支援的平台："
          docker buildx inspect | grep -i platforms || true
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 10: Multi-Arch Docker Build
      # 說明：使用 buildx 同時建構 amd64 和 arm64
      # ========================================
      - name: 🔨 Multi-Arch Docker Build
        run: |
          echo "========================================"
          echo "🔨 開始 Multi-Arch Docker Build"
          echo "========================================"
          echo "版本: v${{ github.event.inputs.image_tag }}"
          echo "映像名稱: ${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"

          # 判斷是否為正式版本（純數字+點號，支援任意段數如 0.1.10 或 0.1.10.1）
          IMAGE_TAG="${{ github.event.inputs.image_tag }}"
          if [[ "$IMAGE_TAG" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then
            echo "✅ 正式版本，將同時標記 latest"
            ADD_LATEST="true"
          else
            echo "⚠️ 測試版本，不標記 latest"
            ADD_LATEST="false"
          fi

          echo "目標架構: linux/amd64, linux/arm64"
          echo ""

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << REMOTE_EOF
          set -e

          WORKDIR="${{ env.REMOTE_WORKDIR }}"
          PROJECT="${{ env.PROJECT_NAME }}"
          IMAGE="${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"
          IMAGE_LATEST="${{ env.DOCKER_IMAGE_REPO }}:latest"
          BUILDER_NAME="multiarch-builder-${{ github.run_id }}"
          ADD_LATEST="${ADD_LATEST}"

          echo "📂 進入專案目錄..."
          cd "\${WORKDIR}/\${PROJECT}"
          pwd

          echo ""
          echo "🔨 執行 Multi-Arch Docker buildx..."
          echo "映像標籤: \${IMAGE}"
          echo "目標平台: linux/amd64, linux/arm64"
          echo ""

          # 確保使用正確的 builder
          docker buildx use "\${BUILDER_NAME}"

          # 根據版本類型決定是否加入 latest tag
          if [ "\${ADD_LATEST}" = "true" ]; then
            echo "📌 同時標記 latest"
            BUILD_TAGS="-t \${IMAGE} -t \${IMAGE_LATEST}"
          else
            echo "📌 僅標記版本號（測試版本）"
            BUILD_TAGS="-t \${IMAGE}"
          fi

          # Multi-arch build（只建構，不推送）
          if ! docker buildx build \
            --platform linux/amd64,linux/arm64 \
            \${BUILD_TAGS} \
            --progress=plain \
            . 2>&1; then
            
            echo ""
            echo "========================================"
            echo "❌ Docker Build 失敗！"
            echo "========================================"
            echo ""
            echo "🔧 常見問題與解決方案："
            echo ""
            echo "1️⃣ apt-get 安裝失敗（arm64 QEMU 問題）"
            echo "   → 建議：拆分 apt-get install 為多段 RUN"
            echo "   → 或：對 arm64 跳過不穩定套件"
            echo ""
            echo "2️⃣ 網路問題"
            echo "   → 建議：加入 apt-get update --fix-missing"
            echo ""
            echo "3️⃣ 記憶體不足"
            echo "   → 建議：增加 Docker 記憶體限制"
            echo ""
            exit 1
          fi

          echo ""
          echo "✅ Multi-Arch Docker Build 完成！"
          echo ""
          echo "🏗️ 已建構的架構："
          echo "   - linux/amd64"
          echo "   - linux/arm64"
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 11: Docker Push 到 Docker Hub
      # 說明：推送建構好的映像到 Docker Hub
      # ========================================
      - name: 📤 Docker Push 到 Docker Hub
        run: |
          echo "========================================"
          echo "📤 推送映像到 Docker Hub"
          echo "========================================"
          echo "版本: v${{ github.event.inputs.image_tag }}"
          echo "映像: ${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"

          # 判斷是否為正式版本（純數字+點號，支援任意段數如 0.1.10 或 0.1.10.1）
          IMAGE_TAG="${{ github.event.inputs.image_tag }}"
          if [[ "$IMAGE_TAG" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then
            echo "✅ 正式版本，將同時推送 latest"
            ADD_LATEST="true"
          else
            echo "⚠️ 測試版本，不推送 latest"
            ADD_LATEST="false"
          fi
          echo ""

          # 檢查 Docker Hub 登錄狀態，未登錄則重新登錄
          echo "🔐 檢查 Docker Hub 登錄狀態..."
          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << EOF
          if docker info 2>/dev/null | grep -q "Username:"; then
            echo "✅ Docker Hub 已登錄"
          else
            echo "⚠️ Docker Hub 未登錄，正在重新登錄..."
            echo '${{ secrets.DOCKERHUB_TOKEN }}' | docker login -u '${{ secrets.DOCKERHUB_USERNAME }}' --password-stdin
            echo "✅ Docker Hub 重新登錄成功"
          fi
          EOF

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << REMOTE_EOF
          set -e

          WORKDIR="${{ env.REMOTE_WORKDIR }}"
          PROJECT="${{ env.PROJECT_NAME }}"
          IMAGE="${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"
          IMAGE_LATEST="${{ env.DOCKER_IMAGE_REPO }}:latest"
          BUILDER_NAME="multiarch-builder-${{ github.run_id }}"
          ADD_LATEST="${ADD_LATEST}"

          echo "📂 進入專案目錄..."
          cd "\${WORKDIR}/\${PROJECT}"

          echo ""
          echo "📤 執行 Docker Push..."
          echo "映像標籤: \${IMAGE}"

          # 確保使用正確的 builder
          docker buildx use "\${BUILDER_NAME}"

          # 根據版本類型決定是否加入 latest tag
          if [ "\${ADD_LATEST}" = "true" ]; then
            echo "同時推送: \${IMAGE_LATEST}"
            BUILD_TAGS="-t \${IMAGE} -t \${IMAGE_LATEST}"
          else
            echo "（測試版本，不推送 latest）"
            BUILD_TAGS="-t \${IMAGE}"
          fi
          echo ""

          # 使用 buildx build --push 來推送 multi-arch 映像
          if ! docker buildx build \
            --platform linux/amd64,linux/arm64 \
            \${BUILD_TAGS} \
            --push \
            --progress=plain \
            . 2>&1; then
            
            echo ""
            echo "========================================"
            echo "❌ Docker Push 失敗！"
            echo "========================================"
            echo ""
            echo "🔧 可能的原因："
            echo "  1. Docker Hub 登入已過期"
            echo "  2. 網路連線問題"
            echo "  3. Docker Hub 配額限制"
            echo ""
            exit 1
          fi

          echo ""
          echo "✅ Docker Push 完成！"
          echo ""
          echo "📦 映像已推送至 Docker Hub："
          echo "   \${IMAGE}"
          if [ "\${ADD_LATEST}" = "true" ]; then
            echo "   \${IMAGE_LATEST}"
          fi
          echo ""
          echo "🏗️ 支援的架構："
          echo "   - linux/amd64"
          echo "   - linux/arm64"
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 13: 驗證 Docker Hub 映像
      # 說明：確認映像已成功推送並檢查架構
      # ========================================
      - name: ✅ 驗證 Docker Hub 映像
        run: |
          echo "========================================"
          echo "✅ 驗證 Docker Hub 映像"
          echo "========================================"
          echo "版本: v${{ github.event.inputs.image_tag }}"
          echo "映像: ${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"
          echo ""

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'REMOTE_EOF'
          set -e

          IMAGE="${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"

          echo "📋 檢查映像 manifest（顯示支援的架構）..."
          echo ""

          docker buildx imagetools inspect "${IMAGE}"

          echo ""
          echo "✅ 映像驗證完成"
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 14: 清理本次 Build 資源
      # 說明：只清理本次 build 產生的 builder 和 cache
      # ========================================
      - name: 🧹 清理本次 Build 資源
        if: always()
        run: |
          echo "========================================"
          echo "🧹 清理本次 Build 資源"
          echo "========================================"
          echo "Builder 名稱: multiarch-builder-${{ github.run_id }}"
          echo ""

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'REMOTE_EOF'
          set -e

          BUILDER_NAME="multiarch-builder-${{ github.run_id }}"

          echo "🗑️ 移除本次使用的 buildx builder..."
          if docker buildx rm "${BUILDER_NAME}" 2>/dev/null; then
            echo "✅ Builder ${BUILDER_NAME} 已移除"
          else
            echo "⚠️ Builder 可能已被移除或不存在"
          fi

          echo ""
          echo "🗑️ 清理本次 build 產生的 cache..."
          # 只清理 buildx 的 dangling cache，不影響其他 image
          docker buildx prune -f --filter "until=1h" 2>/dev/null || true
          echo "✅ Build cache 已清理"

          echo ""
          echo "📋 目前剩餘的 builders："
          docker buildx ls

          echo ""
          echo "📦 目前剩餘的 convertx 相關映像："
          docker images | grep convertx || echo "（無本地 convertx 相關映像，因使用 --push 直接推送）"
          REMOTE_EOF

          echo ""
          echo "✅ 清理完成（僅清理本次 build 的資源）"
          echo "========================================"

      # ========================================
      # Step 15: Docker Logout
      # 說明：登出 Docker Hub
      # ========================================
      - name: 🔒 Docker Hub 登出
        if: always()
        run: |
          echo "========================================"
          echo "🔒 Docker Hub 登出"
          echo "========================================"

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "docker logout && echo '✅ 已登出 Docker Hub'" || true

          echo "========================================"

      # ========================================
      # Step 16: 顯示完成摘要
      # 說明：顯示本次 build 的摘要資訊
      # ========================================
      - name: 📋 Build 完成摘要
        run: |
          echo "========================================"
          echo "✅ Multi-Arch Docker Build & Push 完成！"
          echo "========================================"
          echo ""
          echo "🏷️ 版本: v${{ github.event.inputs.image_tag }}"
          echo ""
          echo "📦 映像資訊："
          echo "  - 版本標籤: ${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"

          # 判斷是否為正式版本（純數字+點號）
          IMAGE_TAG="${{ github.event.inputs.image_tag }}"
          if [[ "$IMAGE_TAG" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then
            echo "  - Latest 標籤: ${{ env.DOCKER_IMAGE_REPO }}:latest ✅"
          else
            echo "  - Latest 標籤: （測試版本，未更新）"
          fi

          echo "  - Docker Hub: https://hub.docker.com/r/${{ env.DOCKER_IMAGE_REPO }}/tags"
          echo ""
          echo "🏗️ 支援的架構："
          echo "  - linux/amd64"
          echo "  - linux/arm64"
          echo ""
          echo "🔧 Build 設定："
          echo "  - 遠端主機: ${{ secrets.SSH_HOST }}"
          echo "  - 工作目錄: ${{ env.REMOTE_WORKDIR }}"
          echo "  - 專案名稱: ${{ env.PROJECT_NAME }}"
          echo ""
          echo "📝 使用方式："
          echo "  docker pull ${{ env.DOCKER_IMAGE_REPO }}:${{ github.event.inputs.image_tag }}"
          if [[ "$IMAGE_TAG" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then
            echo "  docker pull ${{ env.DOCKER_IMAGE_REPO }}:latest"
          fi
          echo ""
          echo "========================================"
          echo "🎉 所有步驟已成功完成！"
          echo "========================================"

      # ========================================
      # Step 17: 生成 Changelog
      # 說明：從 git log 生成變更記錄（與 release.yml 邏輯一致）
      # ========================================
      - name: 📝 Generate Changelog
        if: ${{ !contains(github.event.inputs.image_tag, 'test') && !contains(github.event.inputs.image_tag, 'dev') && !contains(github.event.inputs.image_tag, 'alpha') && !contains(github.event.inputs.image_tag, 'beta') }}
        id: changelog
        run: |
          echo "========================================"
          echo "📝 生成 Changelog"
          echo "========================================"

          # 取得完整 git history
          git fetch --unshallow 2>/dev/null || git fetch --all

          # 取得前一個 tag
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")

          if [ -z "$PREVIOUS_TAG" ]; then
            echo "📋 無前一個 tag，取得所有 commits..."
            CHANGELOG=$(git log --pretty=format:"- %s (%h)" HEAD)
          else
            echo "📋 取得 ${PREVIOUS_TAG} 到 HEAD 的 commits..."
            CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${PREVIOUS_TAG}..HEAD)
          fi

          # 建立 changelog 檔案（與 release.yml 風格一致）
          echo "$CHANGELOG" > changelog.txt

          # 加入 Full Changelog 連結
          echo "" >> changelog.txt
          echo "Full Changelog: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...v${{ github.event.inputs.image_tag }}" >> changelog.txt

          echo ""
          echo "✅ Changelog 已生成："
          cat changelog.txt
          echo "========================================"

      # ========================================
      # Step 18: 創建 GitHub Release
      # 說明：為正式版本自動創建 GitHub Release
      # 注意：不使用 generate_release_notes，改用 body_path 提供完整 changelog
      # 注意：如果 Release 已存在會失敗，使用 continue-on-error 跳過
      # ========================================
      - name: 📦 Create GitHub Release
        if: ${{ !contains(github.event.inputs.image_tag, 'test') && !contains(github.event.inputs.image_tag, 'dev') && !contains(github.event.inputs.image_tag, 'alpha') && !contains(github.event.inputs.image_tag, 'beta') }}
        continue-on-error: true
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{ github.event.inputs.image_tag }}
          name: v${{ github.event.inputs.image_tag }}
          body_path: changelog.txt
          draft: false
          prerelease: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      # ========================================
      # Step 19: 更新遠端 ConvertX-CN 服務
      # 說明：拉取最新映像並重新啟動服務（所有步驟完成後執行）
      # ========================================
      - name: 🔄 更新遠端 ConvertX-CN 服務
        run: |
          echo "========================================"
          echo "🔄 更新遠端 ConvertX-CN 服務"
          echo "========================================"
          echo "版本: v${{ github.event.inputs.image_tag }}"
          echo ""

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << REMOTE_EOF
          set -e

          IMAGE_TAG="v${{ github.event.inputs.image_tag }}"

          # 1. 進入專案資料夾
          cd /home/bioailab/miniconda3/lid/app/convertx-cn

          # 2. 停止服務
          echo "📋 停止服務..."
          docker compose down

          # 3. 拉取最新映像檔
          echo ""
          echo "📋 拉取最新映像檔（\${IMAGE_TAG}）..."
          docker compose pull

          # 4. 重新啟動
          echo ""
          echo "📋 重新啟動服務..."
          docker compose up -d

          # 5. 顯示服務狀態與 log
          echo ""
          echo "📋 服務狀態："
          docker compose ps

          echo ""
          echo "📋 服務 log（最近 100 行）："
          docker logs convertx-cn --tail=100

          echo ""
          echo "✅ ConvertX-CN 服務已更新至 \${IMAGE_TAG}"
          REMOTE_EOF

          echo "========================================"

      # ========================================
      # Step 20: Docker 容器 E2E 測試
      # 說明：在已更新的遠端服務上執行完整 E2E 測試
      # ========================================
      - name: 🧪 Docker 容器 E2E 測試
        run: |
          echo "========================================"
          echo "🧪 Docker 容器 E2E 測試（已部署服務）"
          echo "========================================"
          echo "版本: v${{ github.event.inputs.image_tag }}"
          echo ""

          tailscale ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << 'REMOTE_EOF'
          set -e

          CONTAINER_NAME="convertx-cn"

          echo "⏳ 等待服務就緒..."
          for i in {1..60}; do
            if docker exec "${CONTAINER_NAME}" curl -sf http://localhost:3000/healthcheck > /dev/null 2>&1; then
              echo "✅ 服務在 ${i} 秒內就緒"
              break
            fi
            if [ $i -eq 60 ]; then
              echo "❌ 服務啟動超時"
              docker logs "${CONTAINER_NAME}" --tail=50
              exit 1
            fi
            sleep 1
          done

          echo ""
          echo "========================================"
          echo "📋 執行 E2E 轉換測試..."
          echo "========================================"
          FAILED=0
          PASSED=0

          # 測試 1: Inkscape SVG → PNG
          docker exec "${CONTAINER_NAME}" sh -c '
            echo "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"red\"/></svg>" > /tmp/test.svg
            inkscape --export-type=png --export-filename=/tmp/test.png /tmp/test.svg 2>/dev/null
            [ -f /tmp/test.png ] && echo "✅ Inkscape SVG → PNG" || exit 1
          ' && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1))

          # 測試 2: Inkscape SVG → PDF
          docker exec "${CONTAINER_NAME}" sh -c '
            inkscape --export-type=pdf --export-filename=/tmp/test.pdf /tmp/test.svg 2>/dev/null
            [ -f /tmp/test.pdf ] && echo "✅ Inkscape SVG → PDF" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Inkscape SVG → PDF 跳過"

          # 測試 3: Pandoc Markdown → HTML
          docker exec "${CONTAINER_NAME}" sh -c '
            echo "# Test\n\nHello **world**" > /tmp/test.md
            pandoc /tmp/test.md -o /tmp/test.html
            [ -f /tmp/test.html ] && echo "✅ Pandoc Markdown → HTML" || exit 1
          ' && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1))

          # 測試 4: Pandoc Markdown → DOCX
          docker exec "${CONTAINER_NAME}" sh -c '
            pandoc /tmp/test.md -o /tmp/test_pandoc.docx
            [ -f /tmp/test_pandoc.docx ] && echo "✅ Pandoc Markdown → DOCX" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Pandoc DOCX 跳過"

          # 測試 5: Pandoc Markdown → LaTeX
          docker exec "${CONTAINER_NAME}" sh -c '
            pandoc /tmp/test.md -o /tmp/test.tex
            [ -f /tmp/test.tex ] && echo "✅ Pandoc Markdown → LaTeX" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Pandoc LaTeX 跳過"

          # 測試 6: LibreOffice TXT → PDF
          docker exec "${CONTAINER_NAME}" sh -c '
            echo "Test document content" > /tmp/test.txt
            libreoffice --headless --convert-to pdf --outdir /tmp /tmp/test.txt 2>/dev/null
            [ -f /tmp/test.pdf ] && echo "✅ LibreOffice TXT → PDF" || exit 1
          ' && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1))

          # 測試 7: LibreOffice TXT → ODT → DOCX
          docker exec "${CONTAINER_NAME}" sh -c '
            libreoffice --headless --convert-to odt --outdir /tmp /tmp/test.txt 2>/dev/null
            if [ -f /tmp/test.odt ]; then
              libreoffice --headless --convert-to docx --outdir /tmp /tmp/test.odt 2>/dev/null
              [ -f /tmp/test.docx ] && echo "✅ LibreOffice ODT → DOCX" || exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ LibreOffice ODT 跳過"

          # 測試 8: FFmpeg 音頻生成
          docker exec "${CONTAINER_NAME}" sh -c '
            ffmpeg -f lavfi -i "sine=frequency=440:duration=1" -y /tmp/test.wav 2>/dev/null
            [ -f /tmp/test.wav ] && echo "✅ FFmpeg 音頻生成" || exit 1
          ' && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1))

          # 測試 9: FFmpeg WAV → MP3
          docker exec "${CONTAINER_NAME}" sh -c '
            ffmpeg -i /tmp/test.wav -y /tmp/test.mp3 2>/dev/null
            [ -f /tmp/test.mp3 ] && echo "✅ FFmpeg WAV → MP3" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ FFmpeg MP3 跳過"

          # 測試 10: FFmpeg 影片生成
          docker exec "${CONTAINER_NAME}" sh -c '
            ffmpeg -f lavfi -i color=c=red:s=320x240:d=1 -y /tmp/test_video.mp4 2>/dev/null
            [ -f /tmp/test_video.mp4 ] && echo "✅ FFmpeg 影片生成" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ FFmpeg 影片 跳過"

          # 測試 11: FFmpeg MP4 → GIF
          docker exec "${CONTAINER_NAME}" sh -c '
            if [ -f /tmp/test_video.mp4 ]; then
              ffmpeg -i /tmp/test_video.mp4 -vf "fps=10,scale=160:-1" -y /tmp/test_video.gif 2>/dev/null
              [ -f /tmp/test_video.gif ] && echo "✅ FFmpeg MP4 → GIF" || exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ FFmpeg GIF 跳過"

          # 測試 12: ImageMagick 圖片生成
          docker exec "${CONTAINER_NAME}" sh -c '
            convert -size 100x100 xc:blue /tmp/blue.png
            [ -f /tmp/blue.png ] && echo "✅ ImageMagick 圖片生成" || exit 1
          ' && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1))

          # 測試 13: ImageMagick PNG → JPEG
          docker exec "${CONTAINER_NAME}" sh -c '
            convert /tmp/blue.png /tmp/blue.jpg
            [ -f /tmp/blue.jpg ] && echo "✅ ImageMagick PNG → JPEG" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ ImageMagick JPEG 跳過"

          # 測試 14: ImageMagick PNG → WEBP
          docker exec "${CONTAINER_NAME}" sh -c '
            convert /tmp/blue.png /tmp/blue.webp
            [ -f /tmp/blue.webp ] && echo "✅ ImageMagick PNG → WEBP" || exit 1
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ ImageMagick WEBP 跳過"

          # 測試 15: Calibre HTML → EPUB
          docker exec "${CONTAINER_NAME}" sh -c '
            if command -v ebook-convert &> /dev/null; then
              echo "<!DOCTYPE html><html><body><h1>Test</h1></body></html>" > /tmp/book.html
              ebook-convert /tmp/book.html /tmp/book.epub 2>/dev/null
              [ -f /tmp/book.epub ] && echo "✅ Calibre HTML → EPUB" || exit 1
            else
              exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Calibre 跳過"

          # 測試 16: Potrace PBM → SVG
          docker exec "${CONTAINER_NAME}" sh -c '
            if command -v potrace &> /dev/null; then
              echo "P1 4 4 0 1 1 0 1 0 0 1 1 0 0 1 0 1 1 0" > /tmp/test.pbm
              potrace -s -o /tmp/potrace.svg /tmp/test.pbm
              [ -f /tmp/potrace.svg ] && echo "✅ Potrace PBM → SVG" || exit 1
            else
              exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Potrace 跳過"

          # 測試 17: Tesseract OCR
          docker exec "${CONTAINER_NAME}" sh -c '
            if command -v tesseract &> /dev/null; then
              convert -size 200x50 xc:white -font DejaVu-Sans -pointsize 20 -fill black -annotate +10+35 "Test" /tmp/ocr.png 2>/dev/null
              tesseract /tmp/ocr.png /tmp/ocr 2>/dev/null
              [ -f /tmp/ocr.txt ] && echo "✅ Tesseract OCR" || exit 1
            else
              exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Tesseract 跳過"

          # 測試 18: GraphicsMagick
          docker exec "${CONTAINER_NAME}" sh -c '
            if command -v gm &> /dev/null; then
              gm convert -size 100x100 xc:green /tmp/gm.png
              gm convert /tmp/gm.png /tmp/gm.gif
              [ -f /tmp/gm.gif ] && echo "✅ GraphicsMagick PNG → GIF" || exit 1
            else
              exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ GraphicsMagick 跳過"

          # 測試 19: Resvg
          docker exec "${CONTAINER_NAME}" sh -c '
            if command -v resvg &> /dev/null; then
              resvg /tmp/test.svg /tmp/resvg.png
              [ -f /tmp/resvg.png ] && echo "✅ Resvg SVG → PNG" || exit 1
            else
              exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ Resvg 跳過"

          # 測試 20: VIPS
          docker exec "${CONTAINER_NAME}" sh -c '
            if command -v vips &> /dev/null; then
              vips copy /tmp/blue.png /tmp/vips.webp
              [ -f /tmp/vips.webp ] && echo "✅ VIPS PNG → WEBP" || exit 1
            else
              exit 1
            fi
          ' && PASSED=$((PASSED + 1)) || echo "⚠️ VIPS 跳過"

          # 測試 21: API Healthcheck
          if docker exec "${CONTAINER_NAME}" curl -sf http://localhost:3000/healthcheck | grep -q "OK"; then
            echo "✅ API Healthcheck"
            PASSED=$((PASSED + 1))
          else
            echo "❌ API Healthcheck 失敗"
            FAILED=$((FAILED + 1))
          fi

          # 結果摘要
          echo ""
          echo "========================================"
          echo "📊 E2E 測試結果"
          echo "========================================"
          echo "✅ 通過: ${PASSED} 項"
          echo "⚠️ 跳過/失敗: 部分可選測試"
          echo ""

          if [ $FAILED -gt 3 ]; then
            echo "❌ 關鍵測試失敗過多！"
            exit 1
          else
            echo "✅ E2E 測試通過！"
          fi
          echo "========================================"
          REMOTE_EOF

          echo "========================================"
