# =============================================================================
# AWOOOI CD Pipeline - 開發環境 (dev branch)
# =============================================================================
# 流程: Build → Push to Harbor (dev tag) → Deploy to awoooi-dev namespace
# 用途: 驗證修改，確認無誤後才 merge main → 觸發正式環境部署
# 2026-04-01 ogt: 建立開發環境 CI/CD 分離機制

name: CD Pipeline (Dev)

on:
  push:
    branches: [dev]
  workflow_dispatch:

concurrency:
  group: cd-dev-deploy-${{ github.ref }}
  cancel-in-progress: false

env:
  HARBOR: 192.168.0.110:5000
  HARBOR_MIRROR: 192.168.0.110:5001
  SRE_GROUP_CHAT_ID: "-1003711974679"
  OTEL_EXPORTER_OTLP_ENDPOINT: http://192.168.0.188:24318
  OTEL_SERVICE_NAME: awoooi-cd-dev
  OTEL_RESOURCE_ATTRIBUTES: service.version=${{ github.sha }},deployment.environment=dev

jobs:
  build-and-deploy-dev:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Get Commit Info
        id: commit
        run: |
          echo "short_sha=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT
          echo "message=$(git log -1 --pretty=%s | head -c 50)" >> $GITHUB_OUTPUT
          echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT

      - name: Notify Dev Deploy Start
        run: |
          MSG="🔧 <b>[DEV] 部署開始</b>
          ├ 📝 ${{ steps.commit.outputs.message }}
          ├ 🔖 <code>${{ steps.commit.outputs.short_sha }}</code>
          └ 🌿 dev branch"
          if AWOOI_CICD_STATUS=running \
            AWOOI_CICD_STAGE=dev-deploy \
            AWOOI_CICD_JOB_NAME="[DEV] 部署開始" \
            AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
            AWOOI_CICD_SUMMARY="${{ steps.commit.outputs.message }}" \
            scripts/ci/notify-awoooi-cicd.sh; then
            echo "Dev deploy start notification mirrored through AWOOI API"
          else
            printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
              -d "chat_id=${{ env.SRE_GROUP_CHAT_ID }}" \
              -d "parse_mode=HTML" \
              --data-urlencode "text@-"
          fi

      # API 測試 (同 prod CI，確保 dev 也通過)
      - name: Run API Tests
        run: |
          VENV=/opt/api-venv
          HASH_FILE=/opt/api-venv/.deps_hash
          CURRENT_HASH=$(md5sum apps/api/pyproject.toml | awk '{print $1}')

          if [ ! -d "$VENV" ] || [ "$(cat $HASH_FILE 2>/dev/null)" != "$CURRENT_HASH" ]; then
            python3 -m venv $VENV
            source $VENV/bin/activate
            pip install -q uv
            cd apps/api && uv pip install -q -e ".[dev]" && cd -
            echo "$CURRENT_HASH" > $HASH_FILE
          else
            source $VENV/bin/activate
          fi

          cd apps/api
          # 2026-04-22 ogt: DATABASE_URL 改為必填，單元測試需要此 env var 讓 Settings 通過驗證
          DATABASE_URL="${DATABASE_URL:-postgresql+asyncpg://ci:ci@localhost/ci}" \
          pytest tests/ -v --tb=short -x \
            --ignore=tests/test_anomaly_counter.py \
            --ignore=tests/test_global_repair_cooldown.py \
            --ignore=tests/test_redis_multisig.py \
            --ignore=tests/test_model_regression.py \
            --ignore=tests/test_prompt_validation.py \
            2>&1 | tail -50
          echo "✅ API 測試通過"

      - name: Login to Harbor
        run: |
          HARBOR_USERNAME="$(cat <<'AWOOOI_SECRET_HARBOR_USERNAME'
          ${{ secrets.HARBOR_USERNAME }}
          AWOOOI_SECRET_HARBOR_USERNAME
          )"
          HARBOR_PASSWORD="$(cat <<'AWOOOI_SECRET_HARBOR_PASSWORD'
          ${{ secrets.HARBOR_PASSWORD }}
          AWOOOI_SECRET_HARBOR_PASSWORD
          )"
          printf '%s' "$HARBOR_PASSWORD" | docker login "${{ env.HARBOR }}" \
            -u "$HARBOR_USERNAME" \
            --password-stdin

      # Dev API 鏡像：強制重建，不用 cache（確保 models.json 等配置文件更新）
      - name: Build and Push API (Dev)
        run: |
          docker build -f apps/api/Dockerfile \
            --no-cache \
            -t ${{ env.HARBOR }}/awoooi/api:dev-${{ github.sha }} \
            -t ${{ env.HARBOR }}/awoooi/api:dev-latest \
            .
          docker push ${{ env.HARBOR }}/awoooi/api:dev-${{ github.sha }}
          docker push ${{ env.HARBOR }}/awoooi/api:dev-latest
          echo "✅ Dev API 鏡像建置完成"

      # 注入 Dev K8s Secrets
      - name: Inject Dev K8s Secrets
        run: |
          secret_b64() {
            python3 -c 'import base64, sys; data=sys.stdin.buffer.read(); data=data[:-1] if data.endswith(b"\n") else data; sys.stdout.write(base64.b64encode(data).decode())'
          }
          write_deploy_key() {
            mkdir -p ~/.ssh
            umask 077
            cat > ~/.ssh/deploy_key <<'AWOOOI_DEPLOY_KEY'
          ${{ secrets.DEPLOY_SSH_KEY }}
          AWOOOI_DEPLOY_KEY
            chmod 600 ~/.ssh/deploy_key
          }
          TG_BOT_TOKEN_B64="$(secret_b64 <<'AWOOOI_SECRET_TG_BOT_TOKEN'
          ${{ secrets.TELEGRAM_BOT_TOKEN }}
          AWOOOI_SECRET_TG_BOT_TOKEN
          )"
          TG_CHAT_ID_B64="$(secret_b64 <<'AWOOOI_SECRET_SRE_GROUP_CHAT_ID_COMPAT'
          ${{ secrets.SRE_GROUP_CHAT_ID }}
          AWOOOI_SECRET_SRE_GROUP_CHAT_ID_COMPAT
          )"
          NVIDIA_API_KEY_B64="$(secret_b64 <<'AWOOOI_SECRET_NVIDIA_API_KEY'
          ${{ secrets.NVIDIA_API_KEY }}
          AWOOOI_SECRET_NVIDIA_API_KEY
          )"
          GEMINI_API_KEY_B64="$(secret_b64 <<'AWOOOI_SECRET_GEMINI_API_KEY'
          ${{ secrets.GEMINI_API_KEY }}
          AWOOOI_SECRET_GEMINI_API_KEY
          )"

          mkdir -p ~/.ssh
          write_deploy_key
          # Keep deploy-time host keys separate from the runner user's global
          # known_hosts, which is also used by reboot/cold-start checks.
          DEPLOY_KNOWN_HOSTS="${HOME}/.ssh/deploy_known_hosts"
          ssh-keyscan -T 5 -t ed25519,rsa,ecdsa 192.168.0.120 > "${DEPLOY_KNOWN_HOSTS}" 2>/dev/null
          test -s "${DEPLOY_KNOWN_HOSTS}" || { echo "❌ K8S host keyscan failed: 192.168.0.120"; exit 1; }
          SSH_OPTS="-o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=${DEPLOY_KNOWN_HOSTS} -i ~/.ssh/deploy_key"
          # 2026-05-05 Codex: kubectl runs on 120 control-plane. 121 is a
          # worker and its local kubeconfig points at 127.0.0.1:6443.
          ssh $SSH_OPTS wooo@192.168.0.120 << SECRETS
          set -e
          export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

          sudo kubectl patch secret awoooi-secrets -n awoooi-dev --type='json' -p='[
            {"op":"replace","path":"/data/OPENCLAW_TG_BOT_TOKEN","value":"${TG_BOT_TOKEN_B64}"},
            {"op":"replace","path":"/data/OPENCLAW_TG_CHAT_ID","value":"${TG_CHAT_ID_B64}"}
          ]' || echo "⚠️ Telegram Secrets patch 跳過"

          if [ -n "${NVIDIA_API_KEY_B64}" ]; then
            sudo kubectl patch secret awoooi-secrets -n awoooi-dev --type='json' -p='[
              {"op":"replace","path":"/data/NVIDIA_API_KEY","value":"${NVIDIA_API_KEY_B64}"}
            ]' && echo "✅ NVIDIA_API_KEY 已注入 dev"
          fi

          if [ -n "${GEMINI_API_KEY_B64}" ]; then
            sudo kubectl patch secret awoooi-secrets -n awoooi-dev --type='json' -p='[
              {"op":"replace","path":"/data/GEMINI_API_KEY","value":"${GEMINI_API_KEY_B64}"}
            ]' && echo "✅ GEMINI_API_KEY 已注入 dev"
          fi

          echo "✅ Dev Secrets 注入完成"
          SECRETS

      # 部署到 awoooi-dev
      - name: Deploy to Dev K8s
        run: |
          DEPLOY_KNOWN_HOSTS="${HOME}/.ssh/deploy_known_hosts"
          ssh-keyscan -T 5 -t ed25519,rsa,ecdsa 192.168.0.120 > "${DEPLOY_KNOWN_HOSTS}" 2>/dev/null
          test -s "${DEPLOY_KNOWN_HOSTS}" || { echo "❌ K8S host keyscan failed: 192.168.0.120"; exit 1; }
          SSH_OPTS="-o BatchMode=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=${DEPLOY_KNOWN_HOSTS} -i ~/.ssh/deploy_key"
          cat k8s/awoooi-dev/02-configmap.yaml | \
            ssh $SSH_OPTS wooo@192.168.0.120 \
            "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml && sudo kubectl apply -f -"

          ssh $SSH_OPTS wooo@192.168.0.120 << 'DEPLOY'
          set -e
          export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

          sudo kubectl set image deployment/awoooi-api \
            api=192.168.0.110:5000/awoooi/api:dev-${{ github.sha }} \
            -n awoooi-dev

          sudo kubectl rollout status deployment/awoooi-api -n awoooi-dev --timeout=120s
          echo "✅ Dev 部署完成"

          # Health Check
          sleep 10
          HEALTH_PASS=0
          for i in 1 2 3; do
            HTTP_CODE=$(curl -s -w "%{http_code}" -o /dev/null --connect-timeout 10 "http://localhost:32344/api/v1/health")
            if [ "$HTTP_CODE" = "200" ]; then
              echo "✅ Dev API 健康檢查通過 (port 32344)"
              HEALTH_PASS=1
              break
            fi
            echo "⏳ 嘗試 #$i: HTTP $HTTP_CODE，等待 10s..."
            sleep 10
          done
          if [ "$HEALTH_PASS" = "0" ]; then
            echo "❌ Dev API 健康檢查失敗"
            exit 1
          fi
          DEPLOY

      - name: Notify Dev Deploy Success
        run: |
          END_TIME=$(date +%s)
          DURATION=$((END_TIME - ${{ steps.commit.outputs.start_time }}))
          MINUTES=$((DURATION / 60))
          SECONDS=$((DURATION % 60))
          MSG="✅ <b>[DEV] 部署完成</b>
          ├ 📝 ${{ steps.commit.outputs.message }}
          ├ 🔖 <code>${{ steps.commit.outputs.short_sha }}</code>
          ├ ⏱️ 耗時: ${MINUTES}m ${SECONDS}s
          └ 🩺 http://192.168.0.125:32344/api/v1/health"
          if AWOOI_CICD_STATUS=success \
            AWOOI_CICD_STAGE=dev-deploy \
            AWOOI_CICD_JOB_NAME="[DEV] 部署完成" \
            AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
            AWOOI_CICD_DURATION_SECONDS="${DURATION}" \
            AWOOI_CICD_SUMMARY="${{ steps.commit.outputs.message }}" \
            scripts/ci/notify-awoooi-cicd.sh; then
            echo "Dev deploy success notification mirrored through AWOOI API"
          else
            printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
              -d "chat_id=${{ env.SRE_GROUP_CHAT_ID }}" \
              -d "parse_mode=HTML" \
              --data-urlencode "text@-"
          fi

      - name: Notify Dev Deploy Failure
        if: failure()
        run: |
          MSG="❌ <b>[DEV] 部署失敗</b>
          ├ 📝 ${{ steps.commit.outputs.message }}
          ├ 🔖 <code>${{ steps.commit.outputs.short_sha }}</code>
          └ 🔗 <a href=\"http://192.168.0.110:3001/wooo/awoooi/actions\">查看日誌</a>"
          if AWOOI_CICD_STATUS=failed \
            AWOOI_CICD_STAGE=dev-deploy \
            AWOOI_CICD_JOB_NAME="[DEV] 部署失敗" \
            AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
            AWOOI_CICD_SUMMARY="${{ steps.commit.outputs.message }}" \
            scripts/ci/notify-awoooi-cicd.sh; then
            echo "Dev deploy failure notification mirrored through AWOOI API"
          else
            printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
              -d "chat_id=${{ env.SRE_GROUP_CHAT_ID }}" \
              -d "parse_mode=HTML" \
              --data-urlencode "text@-"
          fi
