chore: 可是实现从创建补丁,应用补丁,到回滚补丁整个流程的操作
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
32
readme.md
Normal 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
|
||||||
|
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user