#!/usr/bin/env bash
# mirror-runner.sh — 把已知"runner host 运行时依赖"从老 runner 一键镜像到新 runner
#
# 用法：
#   bash scripts/ops/mirror-runner.sh <OLD_RUNNER_USER@HOST> <NEW_RUNNER_USER@HOST>
#
# 示例（act_runner 都以 ubuntu 跑）：
#   bash scripts/ops/mirror-runner.sh ubuntu@43.166.205.48 ubuntu@43.166.182.155
#
# 镜像清单（来自 docs/ops/02-gitea-config.md § 7 第 6 条）：
#   1. SSH 资料：~/.ssh/config + ~/.ssh/deploy_key + ~/.ssh/known_hosts
#      漏镜像后果：deploy job silent hang
#   2. claude CLI：sudo npm install -g @anthropic-ai/claude-code@<OLD 版本>
#      漏镜像后果：ai-review 挂在 Verify claude CLI step（ERR-20260519-001）
#   3. claude 登录态：~/.claude/.credentials.json + ~/.claude/settings.json
#      漏镜像后果：CLI 装上但 token 用不了
#
# 新增 runner 运行时依赖时，往 mirror_file 调用列表里加一行即可成为团队共识。
#
# 设计原则：
#   - 不破坏新 runner 已有内容：所有目标文件 chmod 收紧到 600/700，覆盖式写入
#   - 不在本机磁盘落 secret：cat | ssh 流式管道，零临时文件
#   - 完整性自检：跑完做 ssh 验证 + claude 实际调用（不是只看 --version）
#   - 不动 Gitea / act_runner 配置：保持现有 systemd unit 不变
#
# 退出码：
#   0  所有项镜像 + 自检通过
#   1  参数错误
#   2  老 runner 探查失败 / required 文件缺失
#   3  某项镜像失败（网络 / 权限）
#   4  自检失败（CLI 装上了但 token 不可用 / SSH key 不可读）

set -euo pipefail

readonly CLAUDE_PKG="@anthropic-ai/claude-code"

if [ $# -ne 2 ]; then
  cat >&2 <<EOF
用法: $0 <OLD_RUNNER_USER@HOST> <NEW_RUNNER_USER@HOST>
示例: $0 ubuntu@43.166.205.48 ubuntu@43.166.182.155
EOF
  exit 1
fi

OLD="$1"
NEW="$2"

step() { printf '\n\033[1;36m[%s]\033[0m %s\n' "$(date +%H:%M:%S)" "$*"; }
ok()   { printf '\033[1;32m  ✓\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m  ⚠\033[0m %s\n' "$*" >&2; }
die()  { printf '\033[1;31m  ✗\033[0m %s\n' "$*" >&2; exit "${2:-3}"; }

# mirror_file <path> <perms> <required|optional>
# 流式从 OLD 拷到 NEW，零本地临时文件；required 缺失则 die，optional 缺失则 warn
mirror_file() {
  local path="$1" perms="$2" required="$3"
  if ssh "$OLD" "test -f $path"; then
    ssh "$OLD" "cat $path" \
      | ssh "$NEW" "cat > $path && chmod $perms $path" \
      || die "镜像 $path 失败" 3
    ok "镜像 $path (chmod $perms)"
  elif [ "$required" = required ]; then
    die "OLD 上没 $path——补齐后重跑（claude 登录态需先在 OLD 上 \`claude /login\`）" 2
  else
    warn "OLD 上没 $path，跳过"
  fi
}

step "0. 探查 OLD runner ($OLD)"
ssh -o BatchMode=yes "$OLD" 'true' 2>/dev/null \
  || die "SSH $OLD 连不上（检查 ssh key / known_hosts）" 2
ok "SSH 连通"

OLD_CLAUDE_VER=$(ssh "$OLD" "command -v claude >/dev/null && claude --version 2>/dev/null | awk '{print \$1}'" || true)
if [ -z "${OLD_CLAUDE_VER:-}" ]; then
  die "$OLD 上没装 claude CLI——这个脚本前提是从已装好的 runner 镜像；先在 OLD 上装好" 2
fi
ok "OLD claude 版本: $OLD_CLAUDE_VER"

step "1. 镜像 SSH 资料 (~/.ssh/{config,deploy_key,known_hosts})"
ssh "$NEW" 'mkdir -p ~/.ssh && chmod 700 ~/.ssh'
mirror_file '~/.ssh/config'      600 optional
mirror_file '~/.ssh/deploy_key'  600 optional
mirror_file '~/.ssh/known_hosts' 644 optional

step "2. 在 NEW 装 claude CLI@$OLD_CLAUDE_VER"
if ssh "$NEW" 'command -v claude >/dev/null && claude --version >/dev/null 2>&1'; then
  NEW_VER=$(ssh "$NEW" 'claude --version | awk "{print \$1}"')
  ok "NEW 已装 claude $NEW_VER（跳过 npm install）"
else
  ssh "$NEW" "sudo npm install -g $CLAUDE_PKG@$OLD_CLAUDE_VER" \
    || die "npm install -g $CLAUDE_PKG 失败（检查 NEW 上 node/npm）" 3
  ok "已装 $CLAUDE_PKG@$OLD_CLAUDE_VER"
fi

step "3. 镜像 claude 登录态 (~/.claude/{.credentials.json,settings.json})"
ssh "$NEW" 'mkdir -p ~/.claude && chmod 700 ~/.claude'
mirror_file '~/.claude/.credentials.json' 600 required
mirror_file '~/.claude/settings.json'     644 optional

step "4. 自检（不是只看 --version，要实际触发 API）"
NEW_CLAUDE_VER=$(ssh "$NEW" 'claude --version' 2>&1 | head -1)
ok "NEW claude --version: $NEW_CLAUDE_VER"

PING_OUTPUT=$(ssh "$NEW" 'timeout 30 claude -p "reply with exactly: PONG" 2>&1' || true)
if echo "$PING_OUTPUT" | grep -q '^PONG'; then
  ok "claude 实测调用通过（PONG 回包）"
else
  die "claude 装好了但 token 用不上：$PING_OUTPUT（OLD 上 credentials 可能过期，重 \`claude /login\` 后重镜像）" 4
fi

if ssh "$NEW" 'test -f ~/.ssh/deploy_key'; then
  OLD_FP=$(ssh "$OLD" 'ssh-keygen -lf ~/.ssh/deploy_key 2>/dev/null | awk "{print \$2}"')
  NEW_FP=$(ssh "$NEW" 'ssh-keygen -lf ~/.ssh/deploy_key 2>/dev/null | awk "{print \$2}"')
  if [ "$OLD_FP" = "$NEW_FP" ] && [ -n "$OLD_FP" ]; then
    ok "deploy_key 指纹一致: $OLD_FP"
  else
    die "deploy_key 指纹不匹配 (OLD=$OLD_FP NEW=$NEW_FP)" 4
  fi
fi

step "全部镜像 + 自检通过"
echo
echo "下一步建议："
echo "  1. 在仓库重跑一次 ai-review（PR 评论 /ai-review 或推空 commit）"
echo "  2. 看 run 是否分到 $NEW 跑通"
echo "  3. 跑通后这套清单就稳了，下次加 runner 直接复用本脚本"
