177 lines
6.0 KiB
Bash
177 lines
6.0 KiB
Bash
#!/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
|
||
|
||
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 <tag>|mark-stable|status|auto-heal]"
|
||
;;
|
||
esac
|