|
|
#!/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 "$@" |