| #!/bin/bash |
|
|
| |
| |
|
|
| 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' |
|
|
| 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 "检查网络连接..." |
| |
| |
| if curl -s --connect-timeout 10 https://huggingface.co >/dev/null 2>&1; then |
| log_success "HuggingFace 连接正常" |
| else |
| log_error "无法连接到 HuggingFace" |
| exit 1 |
| fi |
| } |
|
|
| |
| create_source_backup() { |
| log_info "在源Space创建备份: $SOURCE_SPACE" |
| |
| if [[ "$DRY_RUN" == true ]]; then |
| log_warning "[DRY-RUN] 模拟在 $SOURCE_SPACE 创建备份" |
| return |
| fi |
| |
| |
| |
| |
| local source_url="https://$SOURCE_SPACE.hf.space" |
| log_info "源Space URL: $source_url" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 |
| } |
|
|
| |
| prepare_target_space() { |
| log_info "准备目标Space: $TARGET_SPACE" |
| |
| if [[ "$DRY_RUN" == true ]]; then |
| log_warning "[DRY-RUN] 模拟准备目标Space" |
| return |
| fi |
| |
| |
| local target_url="https://$TARGET_SPACE.hf.space" |
| log_info "目标Space URL: $target_url" |
| |
| |
| log_info "部署基础代码到目标Space..." |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 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 |
| |
| |
| |
| |
| 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 |
| } |
|
|
| |
| restore_to_target() { |
| log_info "恢复数据到目标Space..." |
| |
| if [[ "$DRY_RUN" == true ]]; then |
| log_warning "[DRY-RUN] 模拟恢复数据到目标Space" |
| return |
| fi |
| |
| |
| log_info "上传备份文件到目标Space..." |
| |
| |
| |
| |
| |
| log_info "在目标Space执行数据恢复..." |
| |
| |
| |
| |
| 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" |
| |
| |
| if curl -s --connect-timeout 10 "$target_url/health" >/dev/null 2>&1; then |
| log_success "目标Space健康检查通过" |
| else |
| log_warning "目标Space健康检查失败,请手动验证" |
| fi |
| |
| |
| 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 |
| |
| |
| |
| } |
|
|
| |
| 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 "$@" |