diff --git a/patch_applier.sh b/patch_applier.sh index 7b38339..4733f23 100644 --- a/patch_applier.sh +++ b/patch_applier.sh @@ -304,14 +304,38 @@ create_backup() { mkdir -p "$(dirname "$backup_tar")" local work_dir="$(pwd)" # 默认是当前工作目录 - - if tar -czf "$backup_tar" -C "${work_dir}" "${files_to_backup[@]/#\//}"; then + + # 创建临时目录用于快速构建压缩包 + local temp_dir="/tmp/backup_fast_$$" + mkdir -p "$temp_dir/contents" + + # 快速复制文件到临时目录(使用 cp --parents 保持目录结构) + for file in "${files_to_backup[@]}"; do + if [[ -f "$file" ]]; then + cp --parents "$file" "$temp_dir/contents/" 2>/dev/null || \ + cp "$file" "$temp_dir/contents/$(basename "$file")" + fi + done + + # 复制 changes_file 为 changes.txt + if [[ -f "$changes_file" ]]; then + cp "$changes_file" "$temp_dir/changes.txt" + fi + + # 从临时目录打包(最简单可靠) + if tar -czf "$backup_tar" -C "$temp_dir" .; then local size=$(du -h "$backup_tar" | cut -f1) info "✅ 备份创建完成: $backup_tar ($size)" >&2 + + # 清理临时目录 + rm -rf "$temp_dir" + echo "$backup_tar" return 0 else error "❌ 备份创建失败" >&2 + # 清理临时目录 + rm -rf "$temp_dir" return 1 fi } diff --git a/patch_rollback.sh b/patch_rollback.sh index 680b157..147e396 100644 --- a/patch_rollback.sh +++ b/patch_rollback.sh @@ -108,6 +108,13 @@ release_lock() { info "🔓 释放回滚锁" } +# 路径规范化函数 - 去除多余的斜杠 +normalize_path() { + local path="$1" + # 使用 sed 去除重复的斜杠 + echo "$path" | sed 's|/\+|/|g' +} + # 清理函数 cleanup() { local exit_code=$? @@ -197,19 +204,19 @@ list_rollback_points() { # 文件可读性检查 if [[ ! -r "$info_file" ]]; then - warning "文件不可读: $info_file" >&2 + warn "文件不可读: $info_file" >&2 continue fi # 文件大小检查 if [[ ! -s "$info_file" ]]; then - warning "空文件: $info_file" >&2 + warn "空文件: $info_file" >&2 continue fi # 解析JSON if ! jq -e '.' "$info_file" >/dev/null 2>&1; then - warning "无效的JSON文件: $info_file" >&2 + warn "无效的JSON文件: $info_file" >&2 continue fi @@ -229,7 +236,7 @@ list_rollback_points() { # 验证备份文件 if [[ ! -f "$backup_path" ]]; then - warning "备份文件不存在: $backup_path (来自 $info_file)" >&2 + warn "备份文件不存在: $backup_path (来自 $info_file)" >&2 continue fi @@ -455,7 +462,17 @@ extract_backup() { info "✅ 备份文件提取完成" # 验证提取内容 - local file_count=$(find "$extract_dir" -type f | wc -l) + # 查找 changes.txt 文件是否存在, 如果存在,则说明找到了,如果不存在,则说明备份文件提取失败 + if [[ -f "$extract_dir/changes.txt" ]]; then + info "找到 changes.txt 文件" + else + warn "❌ 备份文件中未找到 changes.txt 文件" + return 1 + fi + + # 查找解压目录下的contents目录,获得文件数量 + local contents_dir=$(normalize_path "$extract_dir/contents") + local file_count=$(find "$contents_dir" -type f | wc -l) if [[ $file_count -gt 0 ]]; then info "找到 $file_count 个备份文件" return 0 @@ -473,6 +490,7 @@ extract_backup() { perform_rollback() { local extract_dir="$1" local dry_run="${2:-false}" + local rollback_info_file="${3:-}" info "🔄 开始执行回滚操作 (干跑模式: $dry_run)" @@ -484,12 +502,23 @@ perform_rollback() { local rollback_count=0 local error_count=0 local skip_count=0 + local deleted_count=0 - # 查找所有备份文件 - find "$extract_dir" -type f | while read backup_file; do - local relative_path="${backup_file#$extract_dir/}" + # 首先处理新增文件的删除 + handle_added_files_deletion "$extract_dir" "$dry_run" + deleted_count=$? + + # 查找所有备份文件并恢复, 备份文件存放于contents目录下 + local contents_dir="$extract_dir/contents" + contents_dir=$(normalize_path "$contents_dir") + + find "$contents_dir" -type f | while read backup_file; do + local relative_path="${backup_file#$contents_dir/}" + relative_path=$(normalize_path "$relative_path") local target_file="$relative_path" + target_file=$(normalize_path "$target_file") local target_dir=$(dirname "$target_file") + target_dir=$(normalize_path "$target_dir") if [[ "$dry_run" == "true" ]]; then info "📋 [干跑] 回滚文件: $relative_path → $target_file" >&2 @@ -529,7 +558,8 @@ perform_rollback() { done info "📊 回滚操作统计:" - info " 成功: $rollback_count" + info " 恢复文件: $rollback_count" + info " 删除新增: $deleted_count" info " 失败: $error_count" info " 跳过: $skip_count" @@ -547,6 +577,90 @@ perform_rollback() { fi } +# 处理新增文件的删除 +handle_added_files_deletion() { + local extract_dir="$1" + local dry_run="$2" + + info "🗑️ 处理新增文件删除..." + + local changes_file="$extract_dir/changes.txt" + + if [[ ! -f "$changes_file" ]]; then + warn "⚠️ changes.txt 文件不存在,无法处理新增文件删除" + return 0 + fi + + local error_count=0 + local skip_count=0 + local deleted_count=0 + + # 处理ADDED类型的文件 + while IFS='|' read -r change_type path extra_info; do + if [[ "$change_type" == "ADDED" ]]; then + local target_file=$(normalize_path "$path") + + if [[ "$dry_run" == "true" ]]; then + info "📋 [干跑] 删除新增文件: $target_file" >&2 + ((deleted_count++)) + continue + fi + + # 检查文件是否存在 + if [[ -f "$target_file" ]]; then + # 备份即将删除的文件 + local backup_target="$TEMP_DIR/added_files_backup/$path" + backup_target=$(normalize_path "$backup_target") + local backup_dir=$(dirname "$backup_target") + backup_dir=$(normalize_path "$backup_dir") + mkdir -p "$backup_dir" + + if cp -p "$target_file" "$backup_target" 2>/dev/null; then + debug "备份新增文件: $target_file" + fi + + # 删除新增文件 + if rm -f "$target_file"; then + info "✅ 删除新增文件: $target_file" >&2 + ((deleted_count++)) + + # 尝试清理空目录 + local target_dir=$(dirname "$target_file") + target_dir=$(normalize_path "$target_dir") + clean_empty_directories_rollback "$target_dir" + else + error_count=$((error_count+1)) + error "❌ 删除新增文件失败: $target_file" >&2 + fi + else + warn "⚠️ 新增文件不存在,无需删除: $target_file" >&2 + ((skip_count++)) + fi + fi + done < "$changes_file" + + return $deleted_count +} + +# 清理空目录(回滚版本) +clean_empty_directories_rollback() { + local dir="$1" + + while [[ "$dir" != "/" ]] && [[ -d "$dir" ]]; do + # 检查目录是否为空(只检查文件,不考虑隐藏文件) + if [[ -z "$(ls -A "$dir" 2>/dev/null)" ]]; then + if rmdir "$dir" 2>/dev/null; then + debug "清理空目录: $dir" + dir=$(dirname "$dir") + else + break # 目录删除失败,停止清理 + fi + else + break # 目录非空,停止清理 + fi + done +} + # 验证回滚结果 verify_rollback() { local extract_dir="$1" @@ -562,16 +676,20 @@ verify_rollback() { local verification_passed=true local verified_count=0 local failed_count=0 + + # 查找所有备份文件并恢复, 备份文件存放于contents目录下 + local contents_dir="$extract_dir/contents" + contents_dir=$(normalize_path "$contents_dir") # 检查所有备份文件是否已正确恢复 - find "$extract_dir" -type f | while read backup_file; do - local relative_path="${backup_file#$extract_dir/}" + find "$contents_dir" -type f | while read backup_file; do + local relative_path="${backup_file#$contents_dir/}" local target_file="$relative_path" if [[ ! -f "$target_file" ]]; then error "❌ 目标文件不存在: $target_file" verification_passed=false - fail_count=$((fail_count+1)) + failed_count=$((failed_count+1)) continue fi @@ -579,7 +697,7 @@ verify_rollback() { if ! cmp -s "$backup_file" "$target_file"; then error "❌ 文件内容不匹配: $relative_path" verification_passed=false - fail_count=$((fail_count+1)) + failed_count=$((failed_count+1)) continue fi @@ -797,7 +915,7 @@ rollback_patch() { fi # 执行回滚操作 - if ! perform_rollback "$extract_dir" "$dry_run"; then + if ! perform_rollback "$extract_dir" "$dry_run" "$rollback_info_file"; then send_rollback_notification "failure" "$rollback_info_json" "回滚操作失败" return 1 fi diff --git a/patch_workflow.sh b/patch_workflow.sh index 1ea2bbf..06acb2e 100644 --- a/patch_workflow.sh +++ b/patch_workflow.sh @@ -109,7 +109,7 @@ rollback_workflow() { # 主函数 main() { case "${1:-}" in - "generate-full") + "generate-workflow") shift full_patch_workflow "$@" ;; @@ -121,10 +121,14 @@ main() { shift "$SCRIPT_DIR/patch_applier.sh" "$@" ;; - "rollback") + "rollback-workflow") shift rollback_workflow "$@" ;; + "rollback") + shift + "$SCRIPT_DIR/patch_rollback.sh" "$@" + ;; "verify") shift "$SCRIPT_DIR/patch_verifier.sh" "$@" diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..105d325 --- /dev/null +++ b/readme.md @@ -0,0 +1,32 @@ +# 企业级补丁管理脚本系统 + + +## Docker 镜像安装 + +```bash +docker compose down -v +docker compose up -d +``` + +## 常用命令测试 + +```bash +# 1. 安装补丁管理系统 +cd /working_dir/scripts && rm -fr /opt/patch-management/ && ./install_patch_system.sh + +# 2. 生成补丁包 +patch-mgmt generate /working_dir/old-src /working_dir/new-src + + +# 3. 验证补丁包 +patch-mgmt verify /opt/patches/patch-upgrade-hotfix-1.0.0-20251118_033107.tar.gz pre-apply +patch-mgmt verify /opt/patches/patch-upgrade-hotfix-1.0.0-20251118_033107.tar.gz post-apply + +# 4. 应用补丁包, 再需要更新的目录下执行 +cd /working_dir/old-src && patch-mgmt apply /opt/patches/patch-upgrade-hotfix-1.0.0-20251118_033107.tar.gz + +# 5. 回滚补丁包 +cd /working_dir/old-src && patch-mgmt rollback -l +cd /working_dir/old-src && patch-mgmt rollback patch-upgrade-hotfix-1.0.0-20251118_033107_20251118_073639.json + +``` \ No newline at end of file