# ═══════════════════════════════════════════════════════════
# 🛠️ GPU 训练机 Bootstrap · training-bootstrap.yml
# ═══════════════════════════════════════════════════════════
# 签发: 铸渊 · ICE-GL-ZY001
# 版权: 国作登字-2026-A-00037559 · TCS-0002∞
#
# 把 server/training-agent/ 下的脚本 SCP 到 GPU 训练机
# (zy-gpu-train · 119.45.160.137)，写入 .env，触发 setup.sh。
# 全程心跳→训练实时仪表盘。
#
# 必需 Secrets:
#   ZY_GPU_HOST            119.45.160.137
#   ZY_GPU_USER            ubuntu
#   ZY_GPU_KEY             SSH 私钥
#   ZY_COS_SECRET_ID       腾讯云 SecretId
#   ZY_COS_SECRET_KEY      腾讯云 SecretKey
#   ZY_DISPATCH_TOKEN      仓库 dispatch PAT (servers→github 心跳所需)
#                          ⚠️ 需要冰朔在 Settings > Secrets 新建一个 PAT,
#                          权限: repo (classic) 或 contents+actions (fine-grained)
#                          作用域: 仅本仓库
# ═══════════════════════════════════════════════════════════

name: 🛠️ 训练机 Bootstrap

# ⚠️ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 2026-05-03 D72：训练机自动同步已停用。母模型训练转交 Notion 侧
# 霜砚 + 服务器直接协作。SSH/SCP 操作必须 confirm_override=true。
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

on:
  workflow_dispatch:
    inputs:
      confirm_override:
        description: '⚠️ 我已确认霜砚同意 + 服务器无运行中训练，重新启用 bootstrap'
        type: boolean
        default: false
      action:
        description: '动作'
        required: true
        default: 'probe'
        type: choice
        options:
          - probe         # 仅探活 SSH（safe）
          - status        # 查询状态（safe）
          - bootstrap     # 上传脚本 + 跑 setup.sh
          - sync-scripts  # 仅上传脚本（不跑 setup）
          - start         # 启动训练（tmux）
          - stop          # 停训练

permissions:
  contents: read

concurrency:
  group: training-bootstrap
  cancel-in-progress: false

jobs:
  guard:
    name: '🛑 同步停用守卫'
    runs-on: ubuntu-latest
    outputs:
      proceed: ${{ steps.check.outputs.proceed }}
    steps:
      - name: 检查覆盖确认
        id: check
        run: |
          ACTION="${{ github.event.inputs.action }}"
          # probe / status 两个只读动作允许通过 (不修改服务器状态)
          if [[ "$ACTION" == "probe" || "$ACTION" == "status" ]]; then
            echo "ℹ️ 只读动作 ($ACTION)，允许执行"
            echo "proceed=true" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          if [[ "${{ github.event.inputs.confirm_override }}" == "true" ]]; then
            echo "✅ 收到覆盖确认 + 写动作 ($ACTION)，继续"
            echo "proceed=true" >> "$GITHUB_OUTPUT"
          else
            echo "🛑 写动作 ($ACTION) 需要勾选 confirm_override，本次中止"
            echo "proceed=false" >> "$GITHUB_OUTPUT"
            {
              echo "## 🛑 训练机 Bootstrap 已停用"
              echo ""
              echo "动作 \`$ACTION\` 会修改服务器状态，已被 D72 决策保护。"
              echo "如需执行，请勾选 \`confirm_override\` 后再触发。"
              echo ""
              echo "只读动作 \`probe\` / \`status\` 不需要勾选。"
            } >> "$GITHUB_STEP_SUMMARY"
          fi

  bootstrap:
    name: '🚀 ${{ github.event.inputs.action }}'
    needs: guard
    if: needs.guard.outputs.proceed == 'true'
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - name: 📥 Checkout
        uses: actions/checkout@v4

      - name: 🔑 配置 SSH
        run: |
          mkdir -p ~/.ssh
          chmod 700 ~/.ssh
          echo "${{ secrets.ZY_GPU_KEY }}" > ~/.ssh/zy_gpu_key
          chmod 600 ~/.ssh/zy_gpu_key
          ssh-keyscan -H "${{ secrets.ZY_GPU_HOST }}" >> ~/.ssh/known_hosts 2>/dev/null

      - name: 🛰️ 探活
        run: |
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no -o ConnectTimeout=15 \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "echo '✅ SSH OK · $(hostname) · uptime:' && uptime && echo '--- nvidia ---' && (nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv,noheader || echo '驱动尚未就绪')"

      - name: 📡 上报 Bootstrap 开始
        if: github.event.inputs.action == 'bootstrap' || github.event.inputs.action == 'start'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          curl -sS -X POST \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: Bearer $GH_TOKEN" \
            -H "X-GitHub-Api-Version: 2022-11-28" \
            "https://api.github.com/repos/${{ github.repository }}/dispatches" \
            -d '{
              "event_type": "training-progress",
              "client_payload": {
                "phase": "bootstrapping",
                "phase_label": "铸渊触发 ${{ github.event.inputs.action }}",
                "level": "info",
                "message": "training-bootstrap.yml run #${{ github.run_number }} 触发动作=${{ github.event.inputs.action }}",
                "health": { "status": "ok", "message": "bootstrap workflow 启动" }
              }
            }' || true

      - name: 📤 上传脚本到服务器
        if: github.event.inputs.action == 'bootstrap' || github.event.inputs.action == 'sync-scripts' || github.event.inputs.action == 'start' || github.event.inputs.action == 'stop'
        run: |
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            'sudo mkdir -p /opt/guanghu/training/configs && sudo chown -R ${{ secrets.ZY_GPU_USER }}:${{ secrets.ZY_GPU_USER }} /opt/guanghu/training'
          scp -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            server/training-agent/progress-reporter.sh \
            server/training-agent/setup.sh \
            server/training-agent/start-training.sh \
            server/training-agent/watch-training-output.sh \
            server/training-agent/download-model.py \
            server/training-agent/preprocess-corpus.py \
            server/training-agent/train.py \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}:/opt/guanghu/training/"
          scp -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            server/training-agent/configs/ds_zero3_offload.json \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}:/opt/guanghu/training/configs/"

      - name: 🔐 写入 .env (服务器侧)
        if: github.event.inputs.action == 'bootstrap' || github.event.inputs.action == 'sync-scripts'
        env:
          ZY_COS_SECRET_ID: ${{ secrets.ZY_COS_SECRET_ID }}
          ZY_COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }}
          ZY_DISPATCH_TOKEN: ${{ secrets.ZY_DISPATCH_TOKEN }}
          REPO_OWNER: ${{ github.repository_owner }}
          REPO_NAME: ${{ github.event.repository.name }}
        run: |
          # 通过 stdin 写,避免在命令行/参数列表中暴露 secrets
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "umask 077 && cat > /opt/guanghu/training/.env && chmod 600 /opt/guanghu/training/.env" <<EOF
          ZY_COS_SECRET_ID=${ZY_COS_SECRET_ID}
          ZY_COS_SECRET_KEY=${ZY_COS_SECRET_KEY}
          ZY_COS_BUCKET=sy-finetune-corpus-1317346199
          ZY_COS_REGION=ap-guangzhou
          GH_REPO_OWNER=${REPO_OWNER}
          GH_REPO_NAME=${REPO_NAME}
          GH_DISPATCH_TOKEN=${ZY_DISPATCH_TOKEN}
          ZY_TRAIN_ROOT=/opt/guanghu/training
          ZY_TRAIN_DATA=/data/guanghu
          EOF
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "chmod +x /opt/guanghu/training/*.sh"

      - name: 🛠️ 执行 setup.sh
        if: github.event.inputs.action == 'bootstrap'
        run: |
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "sudo -E bash /opt/guanghu/training/setup.sh 2>&1 | tee /tmp/zy-bootstrap.log; tail -c 4000 /tmp/zy-bootstrap.log"

      - name: ▶️ 启动训练 (tmux)
        if: github.event.inputs.action == 'start'
        run: |
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "bash /opt/guanghu/training/start-training.sh --tmux"

      - name: ⏹️ 停止训练
        if: github.event.inputs.action == 'stop'
        run: |
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "bash /opt/guanghu/training/start-training.sh --stop"

      - name: 📊 查询状态
        if: github.event.inputs.action == 'status'
        run: |
          ssh -i ~/.ssh/zy_gpu_key -o StrictHostKeyChecking=no \
            "${{ secrets.ZY_GPU_USER }}@${{ secrets.ZY_GPU_HOST }}" \
            "echo '=== 阶段 ===' && cat /opt/guanghu/training/.phase 2>/dev/null || echo idle; \
             echo '=== tmux ===' && (tmux ls 2>/dev/null || echo no-tmux); \
             echo '=== nvidia-smi ===' && nvidia-smi --query-gpu=index,name,utilization.gpu,memory.used,memory.total,temperature.gpu --format=csv 2>/dev/null || echo no-gpu; \
             echo '=== disk ===' && df -h /data/guanghu 2>/dev/null || df -h /; \
             echo '=== last log lines ===' && tail -n 40 /opt/guanghu/training/training.log 2>/dev/null || echo no-log"

      - name: 📝 摘要
        if: always()
        run: |
          {
            echo "## 训练机操作: \`${{ github.event.inputs.action }}\`"
            echo ""
            echo "- 服务器: \`${{ secrets.ZY_GPU_HOST }}\`"
            echo "- 时间: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
            echo "- Run #: ${{ github.run_number }}"
          } >> "$GITHUB_STEP_SUMMARY"
