name: deploy-uat

# 并发治理（Gitea 1.26+ 支持）：同一 ref 多次 push 时只保最新，老的自动 cancel。
# UAT 短时间内连续 deploy 可能撞 PM2/docker，新 deploy 自动取代老的。
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# 同时部署 FFAI Workspace + AIxC Workspace 到各自的 UAT 服务器（并发，~6min vs 之前串行 ~12min）。
# 单 job 内后台执行两个 SSH，任一失败整个 job 失败；两个互不阻塞。
# 之前是 deploy-uat.yml + deploy-aixc-uat.yml 两个 workflow，self-hosted runner 只有 1 个 worker
# → 串行排队。详见 .learnings/ERRORS.md ERR-20260427-018。

permissions:
  contents: read

on:
  push:
    branches:
      - staging

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup deploy SSH
        uses: ./.gitea/actions/setup-deploy-ssh
        with:
          ssh_key: ${{ secrets.DEPLOY_SSH_KEY }}
          hosts: '43.153.69.73 52.234.29.56'

      - name: 并发部署 FFAI + AIxC UAT
        run: |
          set -uo pipefail

          # 部署 FFAI Workspace UAT（后台 + 日志加 [FFAI] 前缀）
          (
            ssh $DEPLOY_SSH_OPTS ubuntu@43.153.69.73 bash -s <<'FFAI_DEPLOY'
          export NVM_DIR="$HOME/.nvm"
          [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
          cd /srv/apps/ffworkspace-test
          CI=true bash scripts/deploy/deploy.sh uat deploy
          FFAI_DEPLOY
          ) 2>&1 | sed 's/^/[FFAI] /' &
          FFAI_PID=$!

          # 部署 AIxC Workspace UAT
          (
            ssh $DEPLOY_SSH_OPTS itadminaixc@52.234.29.56 bash -s <<'AIXC_DEPLOY'
          export NVM_DIR="$HOME/.nvm"
          [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
          cd /srv/apps/aixcworkspace
          sg docker -c 'CI=true bash scripts/deploy/deploy.sh uat deploy'
          AIXC_DEPLOY
          ) 2>&1 | sed 's/^/[AIXC] /' &
          AIXC_PID=$!

          # 等两个都完成；任一失败整个 step 失败
          wait $FFAI_PID; FFAI_RC=$?
          wait $AIXC_PID; AIXC_RC=$?

          echo "===== 部署结果 ====="
          echo "FFAI UAT exit: $FFAI_RC"
          echo "AIxC UAT exit: $AIXC_RC"

          if [ $FFAI_RC -ne 0 ] || [ $AIXC_RC -ne 0 ]; then
            echo "❌ 至少一个目标部署失败"
            exit 1
          fi
          echo "✅ 两个 UAT 目标全部部署成功"

  post-deploy-regression:
    needs: deploy
    runs-on: uat-with-docker
    timeout-minutes: 45
    # 跟 quality-gates::backend-integration 共用全局测试容器 ffoa-test-postgres / ffoa-test-redis，
    # 必须串行；否则对方一上来 docker rm -f 会把正在跑的本 job 整轮 fail-fast 掉。
    # 历史的 `concurrency: shared-test-containers` block 在 Gitea 1.25.x 静默失效（1.26.0+ 才支持），
    # 改用 flock 在 yaml 层做真串行——见下方 "L1 全量跨模块回归" step。
    # 关联：.learnings/ERRORS/ERR-20260506-001.md / 工单 #264
    # L3 兜底：跑全量 L1 跨模块回归 + L1c (UAT 验收库) + 全量 L2 E2E
    # TODO: L2 E2E 需 Playwright MCP 服务可用；当前先跑 L1 + L1c
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install dependencies
        run: |
          export NVM_DIR="$HOME/.nvm"
          [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
          (cd backend && npm install)
          (cd testing && npm install)

      - name: Generate Prisma Client
        run: |
          export NVM_DIR="$HOME/.nvm"
          [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
          cd backend && DATABASE_URL='postgresql://dummy:dummy@localhost:5432/dummy' npx prisma generate

      # 与 quality-gates::backend-integration 用同一个 /tmp/ffoa-test-containers.lock 互斥；
      # 整段 jest run（含内部 reset_test_db_schema）必须在锁内，避免对方 docker rm 撞库。
      - name: L1 全量跨模块回归
        run: |
          export NVM_DIR="$HOME/.nvm"
          [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"

          exec 200>/tmp/ffoa-test-containers.lock
          if ! flock -x -w 1800 200; then
            echo "❌ 1800s 内未取到 ffoa-test 容器锁，疑似前序 job 卡死"
            echo "--- 诊断 ---"
            lsof /tmp/ffoa-test-containers.lock 2>&1 | head -20 || true
            docker ps -a --filter 'name=ffoa-test' --format 'table {{.Names}}\t{{.Status}}' 2>&1 || true
            echo "如需手动恢复（runner 上）：rm -f /tmp/ffoa-test-containers.lock && docker rm -f ffoa-test-postgres ffoa-test-redis"
            exit 1
          fi
          echo "✅ 已取得 ffoa-test 容器锁"

          # 禁止再加 --runInBand（或任何 -- 开头 jest 选项）。
          # run-backend-integration.sh 的"无参数 = batch by module"判定看 $#，
          # 任何 jest 选项都会被误判为"用户指定测试路径"，绕过 batch → OOM。
          # 详见 .learnings/2026-05-11-deploy-uat-l1-oom-batch-bypass.md
          (cd testing && bash scripts/run-backend-integration.sh --force-reset)
          # 锁随 step 退出由 fd 200 关闭自动释放

      # L1c 数据质量校验已从 UAT 部署阶段移除：
      #   - 数据健康度不应阻断 UAT 部署（L1 集成测试已经跑过，部署的是代码不是数据）
      #   - 此步骤之前从未真正连上 UAT 库（CI runner 上没 ffoa-dev-postgres 容器，
      #     默认 DB_CMD 静默失败，所有 check 都返回假阳性 fail）
      #   - L1c 由 nightly-snapshot-check.yml 兜底（用脱敏快照跑），见
      #     docs/standards/05-development-workflow.md 测试金字塔说明

  post-deploy-uat-smoke:
    needs: deploy
    runs-on: ubuntu-latest
    timeout-minutes: 10
    # post-deploy-regression 跑的是 runner 本地测试容器，跟 UAT 服务死活无关；
    # 这个 job 才真正验"UAT 域名打开能用 + 服务起来了"。
    steps:
      - name: 公网 smoke (FFAI + AIxC UAT)
        run: |
          set -e
          FFAI_URL="https://ffworkspace.test.faradayfuture.com"
          AIXC_URL="https://aixcworkspace.test.aixcrypto.ai"

          smoke_one() {
            local name="$1" url="$2"
            echo "=== [$name] $url/ ==="
            local status
            status=$(curl -s -o /dev/null -w "%{http_code}" "$url/")
            echo "status=$status"
            [ "$status" = "200" ] || { echo "❌ $name frontend"; return 1; }

            echo "=== [$name] $url/api/v1/health ==="
            curl -fsS "$url/api/v1/health" | grep -q '"status":"healthy"' || { echo "❌ $name backend health"; return 1; }
          }

          smoke_one FFAI "$FFAI_URL"
          smoke_one AIXC "$AIXC_URL"
          echo "✅ Public smoke passed (FFAI + AIxC)"

      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup deploy SSH
        uses: ./.gitea/actions/setup-deploy-ssh
        with:
          ssh_key: ${{ secrets.DEPLOY_SSH_KEY }}
          hosts: '43.153.69.73 52.234.29.56'

      - name: On-server smoke (FFAI + AIxC UAT)
        run: |
          set -uo pipefail

          echo "=== FFAI on-server smoke ==="
          ssh $DEPLOY_SSH_OPTS ubuntu@43.153.69.73 \
            'cd /srv/apps/ffworkspace-test && bash scripts/ops/smoke-test.sh uat'

          echo "=== AIxC on-server smoke ==="
          ssh $DEPLOY_SSH_OPTS itadminaixc@52.234.29.56 \
            "cd /srv/apps/aixcworkspace && sg docker -c 'bash scripts/ops/smoke-test.sh uat'"

          echo "✅ On-server smoke passed (FFAI + AIxC)"
