#!/bin/bash # ============================================================ # config-protector.sh — 配置保护器 # 作用:在每次修改配置前自动备份 # 修改后健康检查失败 → 自动回滚 # 成功 → 标记为新的稳定点 # 由 health-watchdog.sh 联动触发 # 也支持手动调用:config-protector.sh [snapshot|rollback|status] # ============================================================ set -e CONFIG_DIR="$HOME/.hermes" LOG="$HOME/.hermes/watchdog/config-protector.log" mkdir -p "$(dirname "$LOG")" log() { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOG"; } # === 快照:记录当前配置状态 === snapshot() { cd "$CONFIG_DIR" # 检查是否有未提交的变更 if git diff --quiet && git diff --cached --quiet; then log "✅ 配置无变更,无需快照" return 0 fi # 有变更 → 自动 commit(只跟踪关键配置,绝不用 git add -A 全量) # 2026-08-01 修复:git add -A 曾把 daemon/pid、cron/jobs.json 等运行时噪音纳入版本库, # 导致仓库永远 dirty、snapshot 每 30min 触发、回滚时删除关键文件。 git add SOUL.md AGENTS.md MEMORY.md config.yaml scripts/ skills/ workflows/ plugins/ 2>/dev/null local changed=$(git diff --cached --name-only | head -5) git commit -m "auto-snapshot $(date '+%Y-%m-%d %H:%M:%S')" 2>&1 | tail -1 | log # 2026-09-10: 自动推 Gitea(根治 commit 积压 — 原先只 commit 不 push,积了 18 个未推) # 网络不通(VPN/局域网未连)时静默跳过,不影响快照主流程 if timeout 60 git push origin main >/dev/null 2>&1; then log " → 已推送 Gitea" else log " → push 跳过(网络不通或无变更)" fi log "📸 快照已保存(变更文件:${changed:-无描述})" # 标记最近配置 git tag -f last-config 2>/dev/null return 0 } # === 回滚:恢复到上次稳定点 === rollback() { cd "$CONFIG_DIR" local target="${1:-stable}" if ! git rev-parse --verify "$target" 2>/dev/null; then log "❌ 回滚目标 '$target' 不存在" return 1 fi log "⚠️ 开始回滚到 $target..." # 2026-08-01 安全修复:禁止回滚到过旧的稳定点(会删除新增文件/回退关键配置) # stable tag 曾停在 2026-07-09(23天前),watchdog 触发回滚=灾难。 local target_ts=$(git log -1 --format="%ct" "$target" 2>/dev/null || echo 0) local now_ts=$(date +%s) local age_days=$(( (now_ts - target_ts) / 86400 )) if [ "$age_days" -gt 7 ]; then log "🚨 拒绝回滚:目标 '$target' 已 $age_days 天前(超过 7 天),回滚会丢失近期配置。改用 'rollback last-config'" return 1 fi # 只回滚受保护的关键配置,不删除任何新增文件 # 2026-08-01 修复:git checkout --force 曾硬重置整个工作区到旧 commit,删除 systemd 相关等新增文件 git stash 2>/dev/null || true # 用 restore 只还原关键配置到目标版本,保留所有新增文件 git restore --source="$target" -- \ SOUL.md AGENTS.md MEMORY.md config.yaml scripts/ skills/ workflows/ plugins/ 2>&1 | log log "✅ 已回滚关键配置到 $target(新增文件已保留)" log "📋 受影响文件:$(git diff --name-only HEAD~1..HEAD 2>/dev/null | head -10 || echo '首次回滚')" # 创建回滚事件记录 echo "$(date '+%Y-%m-%dT%H:%M:%S')|rollback|$target" >> "$CONFIG_DIR/watchdog/rollback-history.txt" return 0 } # === 标记当前为稳定 === mark_stable() { cd "$CONFIG_DIR" local tag="stable-$(date '+%Y%m%d-%H%M%S')" git tag -f "$tag" 2>/dev/null git tag -f stable 2>/dev/null log "⭐ 已标记 $tag 为稳定点" return 0 } # === 状态:显示配置保护和最近变更 === status() { cd "$CONFIG_DIR" echo "=== 配置保护状态 ===" echo "仓库: $CONFIG_DIR/.git" echo "最近提交:" git log --oneline -5 2>/dev/null || echo " (无提交)" echo "" echo "未跟踪文件:" git status --short 2>/dev/null | head -10 || echo " 全部已跟踪" echo "" echo "稳定标签:" git tag -l 'stable-*' | sort -r | head -5 2>/dev/null || echo " (无)" echo "" if [ -f "$CONFIG_DIR/watchdog/rollback-history.txt" ]; then echo "回滚历史:" tail -3 "$CONFIG_DIR/watchdog/rollback-history.txt" fi } # === 与看门狗联动:健康检测失败时自动回滚 === auto_rollback_if_unhealthy() { # 由 watchdog 调用,检测上次变更后系统是否正常 cd "$CONFIG_DIR" # 检查最近是否有配置变更(30分钟内) local last_change=$(git log --oneline -1 --format="%ct" HEAD 2>/dev/null || echo 0) local now=$(date +%s) local age=$(( (now - last_change) / 60 )) if [ "$age" -gt 30 ]; then # 上次变更超过30分钟,不触发自动回滚 return 0 fi # 检查关键进程 # 2026-08-01 修复:hermes 从回滚触发列表移除——hermes gateway 重启是正常运维, # 重启瞬间 pgrep miss 会误触发回滚,造成自杀循环(176次崩溃的根因)。 # 只有独立子系统(zhiyid/new-api/bge)挂了才可能因配置变更导致,才触发回滚。 local dead="" for p in zhiyid-new new-api bge_embed_server; do if ! pgrep -f "$p" > /dev/null 2>&1; then dead="$dead $p" fi done if [ -n "$dead" ]; then log "🚨 检测到自动回滚条件:最近配置变更后进程挂了($dead)" log "🔙 自动回滚中..." rollback "stable" log "🔄 回滚完成,重启服务..." # 重启关键服务 systemctl --user restart zhiyid.service bge-embed.service new-api.service 2>/dev/null || true sleep 5 log "✅ 回滚自愈完成" return 0 fi return 0 } # === 主入口 === case "${1:-snapshot}" in snapshot) snapshot ;; rollback) rollback "$2" ;; mark-stable) mark_stable ;; status) status ;; auto-heal) auto_rollback_if_unhealthy ;; *) echo "用法: config-protector.sh [snapshot|rollback |mark-stable|status|auto-heal]" ;; esac