chore: 可是实现从创建补丁,应用补丁,到回滚补丁整个流程的操作

This commit is contained in:
2025-11-18 15:45:39 +08:00
parent abb68003d2
commit 575f44e6ee
4 changed files with 196 additions and 18 deletions

View File

@@ -304,14 +304,38 @@ create_backup() {
mkdir -p "$(dirname "$backup_tar")" mkdir -p "$(dirname "$backup_tar")"
local work_dir="$(pwd)" # 默认是当前工作目录 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) local size=$(du -h "$backup_tar" | cut -f1)
info "✅ 备份创建完成: $backup_tar ($size)" >&2 info "✅ 备份创建完成: $backup_tar ($size)" >&2
# 清理临时目录
rm -rf "$temp_dir"
echo "$backup_tar" echo "$backup_tar"
return 0 return 0
else else
error "❌ 备份创建失败" >&2 error "❌ 备份创建失败" >&2
# 清理临时目录
rm -rf "$temp_dir"
return 1 return 1
fi fi
} }

View File

@@ -108,6 +108,13 @@ release_lock() {
info "🔓 释放回滚锁" info "🔓 释放回滚锁"
} }
# 路径规范化函数 - 去除多余的斜杠
normalize_path() {
local path="$1"
# 使用 sed 去除重复的斜杠
echo "$path" | sed 's|/\+|/|g'
}
# 清理函数 # 清理函数
cleanup() { cleanup() {
local exit_code=$? local exit_code=$?
@@ -197,19 +204,19 @@ list_rollback_points() {
# 文件可读性检查 # 文件可读性检查
if [[ ! -r "$info_file" ]]; then if [[ ! -r "$info_file" ]]; then
warning "文件不可读: $info_file" >&2 warn "文件不可读: $info_file" >&2
continue continue
fi fi
# 文件大小检查 # 文件大小检查
if [[ ! -s "$info_file" ]]; then if [[ ! -s "$info_file" ]]; then
warning "空文件: $info_file" >&2 warn "空文件: $info_file" >&2
continue continue
fi fi
# 解析JSON # 解析JSON
if ! jq -e '.' "$info_file" >/dev/null 2>&1; then if ! jq -e '.' "$info_file" >/dev/null 2>&1; then
warning "无效的JSON文件: $info_file" >&2 warn "无效的JSON文件: $info_file" >&2
continue continue
fi fi
@@ -229,7 +236,7 @@ list_rollback_points() {
# 验证备份文件 # 验证备份文件
if [[ ! -f "$backup_path" ]]; then if [[ ! -f "$backup_path" ]]; then
warning "备份文件不存在: $backup_path (来自 $info_file)" >&2 warn "备份文件不存在: $backup_path (来自 $info_file)" >&2
continue continue
fi fi
@@ -455,7 +462,17 @@ extract_backup() {
info "✅ 备份文件提取完成" 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 if [[ $file_count -gt 0 ]]; then
info "找到 $file_count 个备份文件" info "找到 $file_count 个备份文件"
return 0 return 0
@@ -473,6 +490,7 @@ extract_backup() {
perform_rollback() { perform_rollback() {
local extract_dir="$1" local extract_dir="$1"
local dry_run="${2:-false}" local dry_run="${2:-false}"
local rollback_info_file="${3:-}"
info "🔄 开始执行回滚操作 (干跑模式: $dry_run)" info "🔄 开始执行回滚操作 (干跑模式: $dry_run)"
@@ -484,12 +502,23 @@ perform_rollback() {
local rollback_count=0 local rollback_count=0
local error_count=0 local error_count=0
local skip_count=0 local skip_count=0
local deleted_count=0
# 查找所有备份文件 # 首先处理新增文件的删除
find "$extract_dir" -type f | while read backup_file; do handle_added_files_deletion "$extract_dir" "$dry_run"
local relative_path="${backup_file#$extract_dir/}" 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" local target_file="$relative_path"
target_file=$(normalize_path "$target_file")
local target_dir=$(dirname "$target_file") local target_dir=$(dirname "$target_file")
target_dir=$(normalize_path "$target_dir")
if [[ "$dry_run" == "true" ]]; then if [[ "$dry_run" == "true" ]]; then
info "📋 [干跑] 回滚文件: $relative_path$target_file" >&2 info "📋 [干跑] 回滚文件: $relative_path$target_file" >&2
@@ -529,7 +558,8 @@ perform_rollback() {
done done
info "📊 回滚操作统计:" info "📊 回滚操作统计:"
info " 成功: $rollback_count" info " 恢复文件: $rollback_count"
info " 删除新增: $deleted_count"
info " 失败: $error_count" info " 失败: $error_count"
info " 跳过: $skip_count" info " 跳过: $skip_count"
@@ -547,6 +577,90 @@ perform_rollback() {
fi 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() { verify_rollback() {
local extract_dir="$1" local extract_dir="$1"
@@ -562,16 +676,20 @@ verify_rollback() {
local verification_passed=true local verification_passed=true
local verified_count=0 local verified_count=0
local failed_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 find "$contents_dir" -type f | while read backup_file; do
local relative_path="${backup_file#$extract_dir/}" local relative_path="${backup_file#$contents_dir/}"
local target_file="$relative_path" local target_file="$relative_path"
if [[ ! -f "$target_file" ]]; then if [[ ! -f "$target_file" ]]; then
error "❌ 目标文件不存在: $target_file" error "❌ 目标文件不存在: $target_file"
verification_passed=false verification_passed=false
fail_count=$((fail_count+1)) failed_count=$((failed_count+1))
continue continue
fi fi
@@ -579,7 +697,7 @@ verify_rollback() {
if ! cmp -s "$backup_file" "$target_file"; then if ! cmp -s "$backup_file" "$target_file"; then
error "❌ 文件内容不匹配: $relative_path" error "❌ 文件内容不匹配: $relative_path"
verification_passed=false verification_passed=false
fail_count=$((fail_count+1)) failed_count=$((failed_count+1))
continue continue
fi fi
@@ -797,7 +915,7 @@ rollback_patch() {
fi 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" "回滚操作失败" send_rollback_notification "failure" "$rollback_info_json" "回滚操作失败"
return 1 return 1
fi fi

View File

@@ -109,7 +109,7 @@ rollback_workflow() {
# 主函数 # 主函数
main() { main() {
case "${1:-}" in case "${1:-}" in
"generate-full") "generate-workflow")
shift shift
full_patch_workflow "$@" full_patch_workflow "$@"
;; ;;
@@ -121,10 +121,14 @@ main() {
shift shift
"$SCRIPT_DIR/patch_applier.sh" "$@" "$SCRIPT_DIR/patch_applier.sh" "$@"
;; ;;
"rollback") "rollback-workflow")
shift shift
rollback_workflow "$@" rollback_workflow "$@"
;; ;;
"rollback")
shift
"$SCRIPT_DIR/patch_rollback.sh" "$@"
;;
"verify") "verify")
shift shift
"$SCRIPT_DIR/patch_verifier.sh" "$@" "$SCRIPT_DIR/patch_verifier.sh" "$@"

32
readme.md Normal file
View File

@@ -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
```