name: '📥 国内搬家·从COS恢复'

# ════════════════════════════════════════════════════════════════
# 国内搬家·从 COS 恢复 · migrate-to-cn-restore
# 编号: ZY-WF-MIGRATE-TO-CN-RESTORE
# Sovereign: TCS-0002∞ · 国作登字-2026-A-00037559
# 守护: 铸渊 · ICE-GL-ZY001
#
# 前提:
#   1. 冰朔已经跑过 migrate-to-cn-build.yml, 下载了 Artifact
#   2. 冰朔已经把 lighthouse-migration-*.tar.gz + .sha256 上传到
#      cos://sy-finetune-corpus-1317346199/lighthouse-migration/
#   3. ZY_CN_SERVER_HOST/USER/KEY 已配 (PR-2 之前已配)
#   4. ZY_COS_SECRET_ID/KEY 已配 (训练时已配)
#
# 流程:
#   ① 误触锁校验「从COS恢复」
#   ② check-secrets (cn-domain-deploy + COS) + 选搬家包
#   ③ SSH 装载凭据 + rsync setup-forgejo.sh + 远端脚本
#   ④ scp COS 凭据到 /tmp/.cos-creds (chmod 600, 用完即删)
#   ⑤ ssh 跑 /tmp/migrate-restore.sh (script 走 stdin pipe 上传, 不入 ps)
#   ⑥ 拉回 _logs/migrate-restore-report.md → GH Step Summary
#   ⑦ 清 /tmp/.cos-creds + /tmp/migrate-restore.sh + 本地 SSH 私钥
#
# 误触锁: confirm_phrase = 「从COS恢复」
# 因果链: cc-001 涌现洁净 + cc-003 动态适配 + cc-004 中文一次性
# ════════════════════════════════════════════════════════════════

on:
  workflow_dispatch:
    inputs:
      confirm_phrase:
        description: '误触锁 · 必须输入「从COS恢复」才会真跑, 其他值降级 dry-run'
        required: false
        default: ''
        type: string
      tag:
        description: '指定的搬家包 tag (例: 20260509-1817). 留空 = 用 cos://lighthouse-migration/ 下最新的.'
        required: false
        default: ''
        type: string
      cos_bucket:
        description: 'COS 桶名 (默认 sy-finetune-corpus-1317346199)'
        required: false
        default: 'sy-finetune-corpus-1317346199'
        type: string
      cos_region:
        description: 'COS 区域 (默认 ap-guangzhou)'
        required: false
        default: 'ap-guangzhou'
        type: string
      dry_run:
        description: '只打印计划不真跑 (true/false)'
        required: false
        default: 'false'
        type: choice
        options:
          - 'false'
          - 'true'

permissions:
  contents: read

concurrency:
  group: migrate-to-cn-restore
  cancel-in-progress: false

jobs:
  preflight:
    name: 启动前预检 · 密钥 + 误触锁
    runs-on: ubuntu-latest
    timeout-minutes: 5
    outputs:
      effective_dry_run: ${{ steps.misclick.outputs.effective_dry_run }}
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: 启动前最小密钥校验 (cn-domain-deploy / restore stage)
        env:
          ZY_CN_SERVER_HOST: ${{ secrets.ZY_CN_SERVER_HOST }}
          ZY_CN_SERVER_USER: ${{ secrets.ZY_CN_SERVER_USER }}
          ZY_CN_SERVER_KEY:  ${{ secrets.ZY_CN_SERVER_KEY }}
          ZY_CN_SERVER_PATH: ${{ secrets.ZY_CN_SERVER_PATH }}
          ZY_CN_SSH_PORT:    ${{ secrets.ZY_CN_SSH_PORT }}
          ZY_COS_SECRET_ID:  ${{ secrets.ZY_COS_SECRET_ID }}
          ZY_COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }}
        run: |
          node scripts/preflight/check-secrets.js \
            --workflow cn-domain-deploy \
            --stage restore \
            --target main

      - name: 误触锁 · 必须输入「从COS恢复」
        id: misclick
        env:
          PHRASE: ${{ inputs.confirm_phrase }}
          DRY: ${{ inputs.dry_run }}
        run: |
          set -e
          EFFECTIVE_DRY="$DRY"
          REQUIRED="从COS恢复"
          if [ "$PHRASE" != "$REQUIRED" ]; then
            echo "═══════════════════════════════════════════════"
            echo "  ⚠️  误触保护"
            echo "       要真在 2C2G 域名机上装 Forgejo + push 搬家包,"
            echo "       必须在 confirm_phrase 字段精确输入: 「$REQUIRED」"
            echo "       当前值=「$PHRASE」 → 自动切到 dry-run."
            echo "═══════════════════════════════════════════════"
            EFFECTIVE_DRY="true"
          else
            echo "✅ 已显式确认 (phrase=「$PHRASE」), 即将真跑 restore"
          fi
          echo "effective_dry_run=$EFFECTIVE_DRY" >> "$GITHUB_OUTPUT"

  restore:
    name: 拉包 + 装 Forgejo + push 历史
    needs: preflight
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4

      - name: 装载 SSH 凭据 (写到 2C2G 域名机)
        id: target
        env:
          HOST: ${{ secrets.ZY_CN_SERVER_HOST }}
          USER: ${{ secrets.ZY_CN_SERVER_USER }}
          KEY:  ${{ secrets.ZY_CN_SERVER_KEY }}
          PORT: ${{ secrets.ZY_CN_SSH_PORT }}
          DEPLOY_PATH: ${{ secrets.ZY_CN_SERVER_PATH }}
        run: |
          set -euo pipefail
          if [ -z "$HOST" ] || [ -z "$KEY" ] || [ -z "$USER" ]; then
            echo "❌ ZY_CN_SERVER_{HOST,USER,KEY} 任一为空" >&2; exit 1
          fi
          PORT="${PORT:-22}"
          DEPLOY_PATH="${DEPLOY_PATH:-/data/guanghulab}"
          echo "user=$USER" >> "$GITHUB_OUTPUT"
          echo "port=$PORT" >> "$GITHUB_OUTPUT"
          echo "deploy_path=$DEPLOY_PATH" >> "$GITHUB_OUTPUT"
          umask 077
          mkdir -p ~/.ssh
          printf '%s\n' "$KEY" > ~/.ssh/restore_key
          chmod 600 ~/.ssh/restore_key
          ssh-keyscan -p "$PORT" -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
          printf '%s' "$HOST" > ~/.ssh/restore_host
          chmod 600 ~/.ssh/restore_host
          echo "✅ SSH 凭据就绪"

      - name: Dry-run · 只打印计划
        if: needs.preflight.outputs.effective_dry_run == 'true'
        env:
          USER: ${{ steps.target.outputs.user }}
          DEPLOY_PATH: ${{ steps.target.outputs.deploy_path }}
          BUCKET: ${{ inputs.cos_bucket }}
          REGION: ${{ inputs.cos_region }}
          TAG: ${{ inputs.tag }}
        run: |
          echo "═══════════════ DRY RUN ═══════════════"
          echo "would ssh $USER@<host> :"
          echo "  1. rsync server/setup/domain-cn/forgejo/ → /opt/guanghulab/domain-cn/forgejo/"
          echo "  2. scp scripts/migration/remote/migrate-restore-remote.sh → /tmp/migrate-restore.sh"
          echo "  3. scp COS 凭据 (走 stdin pipe) → /tmp/.cos-creds (chmod 600)"
          echo "  4. sudo bash /tmp/migrate-restore.sh"
          echo "       BUCKET=$BUCKET REGION=$REGION TAG=$TAG DEPLOY_PATH=$DEPLOY_PATH"
          echo "  5. 远端流程 10 步 (装 coscli → 拉包 → 校验 → 解 → forgejo → nginx → push)"
          echo "  6. 拉回 /opt/guanghulab/_logs/migrate-restore-report.md"
          echo "  7. 清 /tmp/.cos-creds + /tmp/migrate-restore.sh"

      - name: Rsync setup-forgejo.sh + remote 脚本上去
        if: needs.preflight.outputs.effective_dry_run != 'true'
        env:
          USER: ${{ steps.target.outputs.user }}
          PORT: ${{ steps.target.outputs.port }}
        run: |
          set -euo pipefail
          HOST="$(cat ~/.ssh/restore_host)"

          ssh -i ~/.ssh/restore_key -p "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST" \
              "sudo mkdir -p /opt/guanghulab/domain-cn/forgejo && sudo chown -R $USER:$USER /opt/guanghulab"

          rsync -avz --delete \
            -e "ssh -i ~/.ssh/restore_key -p $PORT -o StrictHostKeyChecking=accept-new" \
            server/setup/domain-cn/forgejo/ \
            "$USER@$HOST:/opt/guanghulab/domain-cn/forgejo/"

          # 把远端脚本传上去 (走 scp), 不通过命令行参数
          scp -i ~/.ssh/restore_key -P "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              scripts/migration/remote/migrate-restore-remote.sh \
              "$USER@$HOST:/tmp/migrate-restore.sh"
          ssh -i ~/.ssh/restore_key -p "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST" "chmod 700 /tmp/migrate-restore.sh"

      - name: 上传 COS 凭据 (走 SSH stdin pipe, 不入 ps)
        if: needs.preflight.outputs.effective_dry_run != 'true'
        env:
          USER: ${{ steps.target.outputs.user }}
          PORT: ${{ steps.target.outputs.port }}
          COS_SECRET_ID: ${{ secrets.ZY_COS_SECRET_ID }}
          COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }}
        run: |
          set -euo pipefail
          HOST="$(cat ~/.ssh/restore_host)"
          # secrets 通过 stdin → cat > 文件, 不出现在 ssh 命令行里 (避免 ps 暴露)
          # 用 printf 而非 here-doc, 避免 YAML 块标量缩进与 here-doc 终结符冲突
          printf 'COS_SECRET_ID=%s\nCOS_SECRET_KEY=%s\n' "$COS_SECRET_ID" "$COS_SECRET_KEY" \
            | ssh -i ~/.ssh/restore_key -p "$PORT" \
                -o StrictHostKeyChecking=accept-new \
                "$USER@$HOST" \
                "umask 077 && cat > /tmp/.cos-creds && chmod 600 /tmp/.cos-creds"
          # 移到 root 名下 (脚本 sudo 跑, 读 /tmp/.cos-creds 需要可读)
          ssh -i ~/.ssh/restore_key -p "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST" \
              "sudo chown root:root /tmp/.cos-creds && sudo chmod 600 /tmp/.cos-creds"

      - name: 真跑远端 restore
        if: needs.preflight.outputs.effective_dry_run != 'true'
        env:
          USER: ${{ steps.target.outputs.user }}
          PORT: ${{ steps.target.outputs.port }}
          DEPLOY_PATH: ${{ steps.target.outputs.deploy_path }}
          BUCKET: ${{ inputs.cos_bucket }}
          REGION: ${{ inputs.cos_region }}
          TAG: ${{ inputs.tag }}
        run: |
          set -euo pipefail
          # 校验所有上游 input · sudo env 之前必须 whitelist · 防止注入
          # TAG 允许空 (= 用最新), 或 alphanumeric + - + _ + . + 中间 *
          if ! printf '%s' "$TAG" | grep -qE '^[0-9A-Za-z._*-]*$'; then
            echo "❌ TAG 含非法字符: $TAG (只允许 [0-9A-Za-z._*-])" >&2; exit 1
          fi
          if ! printf '%s' "$BUCKET" | grep -qE '^[0-9a-z][0-9a-z.-]{2,62}$'; then
            echo "❌ BUCKET 不符合 COS 桶名规范: $BUCKET" >&2; exit 1
          fi
          if ! printf '%s' "$REGION" | grep -qE '^ap-[a-z]+(-[0-9]+)?$'; then
            echo "❌ REGION 不符合腾讯云 region 规范: $REGION" >&2; exit 1
          fi
          if ! printf '%s' "$DEPLOY_PATH" | grep -qE '^/[A-Za-z0-9_/-]+$'; then
            echo "❌ DEPLOY_PATH 不是绝对路径或含非法字符: $DEPLOY_PATH" >&2; exit 1
          fi

          HOST="$(cat ~/.ssh/restore_host)"
          ssh -i ~/.ssh/restore_key -p "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST" \
              "sudo env BUCKET='$BUCKET' REGION='$REGION' TAG='$TAG' DEPLOY_PATH='$DEPLOY_PATH' bash /tmp/migrate-restore.sh" \
              2>&1 | tee restore-output.log

      - name: 远端清理 (无论成败都跑)
        if: needs.preflight.outputs.effective_dry_run != 'true' && always()
        env:
          USER: ${{ steps.target.outputs.user }}
          PORT: ${{ steps.target.outputs.port }}
        run: |
          set -uo pipefail
          HOST="$(cat ~/.ssh/restore_host)"
          ssh -i ~/.ssh/restore_key -p "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST" \
              "sudo rm -f /tmp/.cos-creds /tmp/migrate-restore.sh" || true

      - name: 拉回中文回执
        if: needs.preflight.outputs.effective_dry_run != 'true' && always()
        env:
          USER: ${{ steps.target.outputs.user }}
          PORT: ${{ steps.target.outputs.port }}
        run: |
          set -uo pipefail
          HOST="$(cat ~/.ssh/restore_host)"
          mkdir -p artifacts
          scp -i ~/.ssh/restore_key -P "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST:/opt/guanghulab/_logs/migrate-restore-report.md" \
              artifacts/ 2>/dev/null || echo "(no migrate-restore-report.md)"
          scp -i ~/.ssh/restore_key -P "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST:/opt/guanghulab/_logs/migrate-restore-*.json" \
              artifacts/ 2>/dev/null || true
          scp -i ~/.ssh/restore_key -P "$PORT" \
              -o StrictHostKeyChecking=accept-new \
              "$USER@$HOST:/opt/guanghulab/_logs/forgejo-setup-*.json" \
              artifacts/ 2>/dev/null || true
          ls -la artifacts/ || true

      - name: 把中文回执贴到 GitHub Actions Summary
        if: always()
        run: |
          set -uo pipefail
          {
            echo "# 📥 国内搬家·从 COS 恢复 · 回执"
            echo ""
            echo "> 给冰朔看的中文回执 · 守护: 铸渊 · ICE-GL-ZY001"
            echo ""
            if [ "${{ needs.preflight.outputs.effective_dry_run }}" = "true" ]; then
              echo "## ⚠️ DRY RUN (没真在域名机上动手)"
              echo ""
              echo "误触锁口令没填对, 工作流只打印了计划. 要真跑 confirm_phrase 必须填: \`从COS恢复\`"
              echo ""
            else
              if [ -f artifacts/migrate-restore-report.md ]; then
                cat artifacts/migrate-restore-report.md
              else
                echo "## ⚠️ 没找到远端回执 \`migrate-restore-report.md\`"
                echo ""
                echo "可能远端 /tmp/migrate-restore.sh 半路死了. 看下面的 restore-output.log artifact."
              fi
            fi
            echo ""
            echo "---"
            echo ""
            echo "## 🎯 接下来 (恢复成功后)"
            echo ""
            echo "1. 浏览器开 \`https://guanghulab.com/git\`"
            echo "2. SSH 到 2C2G 看 \`/data/guanghulab/_logs/forgejo-credentials-FIRST-BOOT.txt\` 抄密码"
            echo "3. **抄完后 \`sudo rm\` 删那个凭据文件** — 安全要求 (cc-001 涌现洁净)"
          } >> "$GITHUB_STEP_SUMMARY"

      - name: 上传 artifact
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: migrate-to-cn-restore-${{ github.run_id }}
          path: |
            artifacts/
            restore-output.log
          retention-days: 30
          if-no-files-found: warn

      - name: 清理本地 SSH 私钥
        if: always()
        run: |
          rm -f ~/.ssh/restore_key ~/.ssh/restore_host
