#!/bin/bash # OCNGX 迁移工具 # 用于在不同 HuggingFace Spaces 之间快速迁移系统 set -euo pipefail # 配置变量 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" MIGRATION_LOG="/tmp/ocngx_migration_$(date +%s).log" # 颜色输出 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$MIGRATION_LOG" } log_info() { echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$MIGRATION_LOG" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$MIGRATION_LOG" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$MIGRATION_LOG" } log_error() { echo -e "${RED}[ERROR]${NC} $1" | tee -a "$MIGRATION_LOG" } # 显示帮助信息 usage() { echo "OCNGX 迁移工具 - 在不同 HuggingFace Spaces 之间迁移系统" echo "" echo "用法: $0 [选项] <源Space> <目标Space>" echo "" echo "参数:" echo " <源Space> 源 HuggingFace Space 名称" echo " <目标Space> 目标 HuggingFace Space 名称" echo "" echo "选项:" echo " -b, --backup-only 仅创建备份,不执行迁移" echo " -r, --restore-only 仅恢复备份,不创建新备份" echo " -k, --keep-backup 保留本地备份文件" echo " -f, --force 强制覆盖目标配置" echo " -d, --dry-run 模拟运行,不执行实际操作" echo " -h, --help 显示帮助信息" echo "" echo "示例:" echo " $0 source-space target-space" echo " $0 --backup-only source-space" echo " $0 --restore-only --force target-space backup.tar.gz" echo "" exit 1 } # 解析命令行参数 SOURCE_SPACE="" TARGET_SPACE="" BACKUP_ONLY=false RESTORE_ONLY=false KEEP_BACKUP=false FORCE_MODE=false DRY_RUN=false BACKUP_FILE="" while [[ $# -gt 0 ]]; do case $1 in -b|--backup-only) BACKUP_ONLY=true shift ;; -r|--restore-only) RESTORE_ONLY=true shift ;; -k|--keep-backup) KEEP_BACKUP=true shift ;; -f|--force) FORCE_MODE=true shift ;; -d|--dry-run) DRY_RUN=true shift ;; -h|--help) usage ;; -*) log_error "未知选项: $1" usage ;; *) if [[ -z "$SOURCE_SPACE" ]]; then SOURCE_SPACE="$1" elif [[ -z "$TARGET_SPACE" ]]; then TARGET_SPACE="$1" elif [[ -z "$BACKUP_FILE" ]]; then BACKUP_FILE="$1" else log_error "参数过多" usage fi shift ;; esac done # 验证参数 validate_parameters() { log_info "验证参数..." if [[ "$RESTORE_ONLY" == true ]]; then if [[ -z "$TARGET_SPACE" && -z "$BACKUP_FILE" ]]; then log_error "恢复模式需要指定目标Space或备份文件" usage fi else if [[ -z "$SOURCE_SPACE" || -z "$TARGET_SPACE" ]]; then log_error "必须指定源Space和目标Space" usage fi fi log_success "参数验证通过" } # 检测当前环境 detect_current_environment() { log_info "检测当前环境..." if [[ -n "${SPACE_ID:-}" ]]; then CURRENT_ENV="huggingface" CURRENT_SPACE="$SPACE_ID" log_info "当前环境: HuggingFace Space ($CURRENT_SPACE)" elif [[ -f /.dockerenv ]]; then CURRENT_ENV="docker" CURRENT_SPACE="docker-container" log_info "当前环境: Docker 容器" else CURRENT_ENV="local" CURRENT_SPACE="local-machine" log_info "当前环境: 本地机器" fi } # 检查网络连接 check_network_connectivity() { log_info "检查网络连接..." # 检查 HuggingFace 连通性 if curl -s --connect-timeout 10 https://huggingface.co >/dev/null 2>&1; then log_success "HuggingFace 连接正常" else log_error "无法连接到 HuggingFace" exit 1 fi } # 创建源Space备份 create_source_backup() { log_info "在源Space创建备份: $SOURCE_SPACE" if [[ "$DRY_RUN" == true ]]; then log_warning "[DRY-RUN] 模拟在 $SOURCE_SPACE 创建备份" return fi # 这里需要SSH或API方式连接到源Space # 实际实现取决于具体的访问方式 local source_url="https://$SOURCE_SPACE.hf.space" log_info "源Space URL: $source_url" # 方案1: 通过SSH访问(如果支持) # ssh user@$SOURCE_SPACE.hf.space "./backup-scripts/backup.sh" # 方案2: 通过API触发备份 # curl -X POST "$source_url/api/backup" # 方案3: 通过HuggingFace CLI # huggingface-cli space create-backup $SOURCE_SPACE # 临时方案: 假设可以SSH访问 log_warning "需要手动在源Space执行: ./backup-scripts/backup.sh" log_info "等待用户确认备份已完成..." read -p "确认备份已完成? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then log_success "源Space备份已确认" else log_error "备份未完成,终止迁移" exit 1 fi } # 下载备份文件 download_backup() { log_info "下载备份文件..." if [[ "$DRY_RUN" == true ]]; then log_warning "[DRY-RUN] 模拟下载备份文件" return fi # 这里需要实现从源Space下载备份的逻辑 # 可能的实现方式: # 1. 通过SCP下载 # scp user@$SOURCE_SPACE.hf.space:/var/backups/ocngx/latest.tar.gz ./ # 2. 通过HTTP下载 # wget "$source_url/download/backup/latest.tar.gz" # 3. 通过HuggingFace API下载 # huggingface-cli space download-backup $SOURCE_SPACE # 临时方案: 假设用户已手动下载 log_warning "需要手动从源Space下载备份文件到当前目录" log_info "备份文件通常位于: /var/backups/ocngx/ocngx_backup_*.tar.gz" read -p "请输入备份文件路径: " backup_path if [[ -f "$backup_path" ]]; then BACKUP_FILE="$backup_path" log_success "备份文件已找到: $BACKUP_FILE" else log_error "备份文件不存在: $backup_path" exit 1 fi } # 准备目标Space prepare_target_space() { log_info "准备目标Space: $TARGET_SPACE" if [[ "$DRY_RUN" == true ]]; then log_warning "[DRY-RUN] 模拟准备目标Space" return fi # 1. 检查目标Space是否存在 local target_url="https://$TARGET_SPACE.hf.space" log_info "目标Space URL: $target_url" # 2. 部署基础代码到目标Space log_info "部署基础代码到目标Space..." # 这里需要实现部署逻辑 # 可能的实现方式: # 1. 通过Git部署 # git clone <项目仓库> target-deploy # cd target-deploy # git remote set-url origin git@hf.co:spaces/$TARGET_SPACE # git push origin main # 2. 通过HuggingFace CLI部署 # huggingface-cli space create $TARGET_SPACE --space-type docker # 临时方案: 假设用户已手动部署 log_warning "需要手动将项目代码部署到目标Space: $TARGET_SPACE" log_info "步骤:" log_info "1. 创建新的Space: https://huggingface.co/new-space" log_info "2. 克隆当前项目代码" log_info "3. 推送到新Space" read -p "确认目标Space已准备完成? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then log_success "目标Space准备已确认" else log_error "目标Space未准备完成,终止迁移" exit 1 fi } # 配置目标环境 configure_target_environment() { log_info "配置目标环境..." if [[ "$DRY_RUN" == true ]]; then log_warning "[DRY-RUN] 模拟配置目标环境" return fi # 这里需要在目标Space上执行环境配置 # 类似于 prepare_target_space 的逻辑 log_warning "需要手动在目标Space配置环境:" log_info "1. SSH连接到目标Space" log_info "2. 设置环境变量: export SPACE_ID=$TARGET_SPACE" log_info "3. 运行: source deploy-config.sh && configure_for_deployment" read -p "确认环境配置已完成? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then log_success "目标环境配置已确认" else log_error "环境配置未完成,终止迁移" exit 1 fi } # 恢复数据到目标Space restore_to_target() { log_info "恢复数据到目标Space..." if [[ "$DRY_RUN" == true ]]; then log_warning "[DRY-RUN] 模拟恢复数据到目标Space" return fi # 上传备份文件到目标Space log_info "上传备份文件到目标Space..." # 实现上传逻辑 # scp backup.tar.gz user@$TARGET_SPACE.hf.space:/tmp/ # 在目标Space执行恢复 log_info "在目标Space执行数据恢复..." # 实现恢复逻辑 # ssh user@$TARGET_SPACE.hf.space "./backup-scripts/restore.sh --force /tmp/backup.tar.gz" log_warning "需要手动在目标Space执行:" log_info "1. 上传备份文件: scp $BACKUP_FILE user@$TARGET_SPACE.hf.space:/tmp/" log_info "2. SSH连接到目标Space" log_info "3. 运行: ./backup-scripts/restore.sh --force /tmp/$(basename $BACKUP_FILE)" read -p "确认数据恢复已完成? (y/N): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then log_success "数据恢复已确认" else log_error "数据恢复未完成,终止迁移" exit 1 fi } # 验证迁移结果 verify_migration() { log_info "验证迁移结果..." local target_url="https://$TARGET_SPACE.hf.space" # 检查目标Space健康状态 if curl -s --connect-timeout 10 "$target_url/health" >/dev/null 2>&1; then log_success "目标Space健康检查通过" else log_warning "目标Space健康检查失败,请手动验证" fi # 检查OpenCode状态 if curl -s --connect-timeout 10 "$target_url/global/health" >/dev/null 2>&1; then log_success "OpenCode服务状态正常" else log_warning "OpenCode服务状态异常,请手动检查" fi } # 清理临时文件 cleanup() { log_info "清理临时文件..." if [[ "$KEEP_BACKUP" != true ]] && [[ -n "$BACKUP_FILE" ]] && [[ -f "$BACKUP_FILE" ]]; then if [[ "$DRY_RUN" != true ]]; then rm -f "$BACKUP_FILE" log_success "已清理备份文件: $BACKUP_FILE" fi fi # 清理日志文件 # rm -f "$MIGRATION_LOG" } # 生成迁移报告 generate_report() { log_info "生成迁移报告..." local report_file="migration_report_$(date +%Y%m%d_%H%M%S).txt" cat > "$report_file" << EOF OCNGX 迁移报告 =============== 迁移时间: $(date '+%Y-%m-%d %H:%M:%S') 源Space: $SOURCE_SPACE 目标Space: $TARGET_SPACE 备份文件: $BACKUP_FILE 当前环境: $CURRENT_ENV ($CURRENT_SPACE) 迁移参数: - 强制模式: $FORCE_MODE - 仅备份: $BACKUP_ONLY - 仅恢复: $RESTORE_ONLY - 保留备份: $KEEP_BACKUP - 模拟运行: $DRY_RUN 迁移日志: $(cat "$MIGRATION_LOG") 后续步骤: 1. 访问目标Space: https://$TARGET_SPACE.hf.space 2. 验证所有功能正常 3. 更新DNS或代理配置(如需要) 4. 通知用户迁移完成 注意事项: - 请保留此报告用于记录 - 建议在迁移后24小时内监控系统状态 - 如有问题,请检查迁移日志 EOF log_success "迁移报告已生成: $report_file" } # 主迁移流程 execute_migration() { log_info "开始迁移流程..." log_info "源Space: $SOURCE_SPACE -> 目标Space: $TARGET_SPACE" if [[ "$BACKUP_ONLY" == true ]]; then create_source_backup download_backup log_success "备份完成" return fi if [[ "$RESTORE_ONLY" == true ]]; then if [[ -z "$TARGET_SPACE" ]]; then # 本地恢复模式 log_info "本地恢复模式" if [[ -z "$BACKUP_FILE" ]]; then log_error "本地恢复需要指定备份文件" exit 1 fi ./backup-scripts/restore.sh ${FORCE_MODE:+--force} "$BACKUP_FILE" else prepare_target_space configure_target_environment restore_to_target fi verify_migration log_success "恢复完成" return fi # 完整迁移流程 create_source_backup download_backup prepare_target_space configure_target_environment restore_to_target verify_migration log_success "迁移完成!" } # 错误处理 handle_error() { log_error "迁移过程中发生错误" cleanup exit 1 } # 主函数 main() { echo "🚀 OCNGX 迁移工具" echo "==================" validate_parameters detect_current_environment check_network_connectivity trap handle_error ERR execute_migration generate_report cleanup echo "" log_success "迁移流程执行完成!" echo "📊 迁移日志: $MIGRATION_LOG" echo "📋 如有疑问,请查看迁移报告" } # 执行主函数 main "$@"