Some checks failed
Architecture Lint / Lint Repository (push) Has been cancelled
Architecture Lint / Linter Tests (push) Has been cancelled
Smoke Test / smoke (push) Has been cancelled
Validate Config / Python Syntax & Import Check (push) Has been cancelled
Validate Config / Python Test Suite (push) Has been cancelled
Validate Config / Shell Script Lint (push) Has been cancelled
Validate Config / Cron Syntax Check (push) Has been cancelled
Validate Config / Deploy Script Dry Run (push) Has been cancelled
Validate Config / Playbook Schema Validation (push) Has been cancelled
Validate Config / YAML Lint (push) Has started running
Validate Config / JSON Validate (push) Has started running
Merge PR #525
160 lines
4.9 KiB
Bash
160 lines
4.9 KiB
Bash
#!/usr/bin/env bash
|
|
# auto-commit-guard.sh — Background daemon that auto-commits uncommitted work
|
|
#
|
|
# Usage: auto-commit-guard.sh [interval_seconds] [worktree_base]
|
|
# auto-commit-guard.sh # defaults: 120s, ~/worktrees
|
|
# auto-commit-guard.sh 60 # check every 60s
|
|
# auto-commit-guard.sh 180 ~/my-worktrees
|
|
#
|
|
# Scans all git repos under the worktree base for uncommitted changes.
|
|
# If dirty for >= 1 check cycle, auto-commits with a WIP message.
|
|
# Pushes unpushed commits so work is always recoverable from the remote.
|
|
#
|
|
# Also scans /tmp for orphaned agent workdirs on startup.
|
|
|
|
set -uo pipefail
|
|
|
|
INTERVAL="${1:-120}"
|
|
WORKTREE_BASE="${2:-$HOME/worktrees}"
|
|
LOG_DIR="$HOME/.hermes/logs"
|
|
LOG="$LOG_DIR/auto-commit-guard.log"
|
|
PIDFILE="$LOG_DIR/auto-commit-guard.pid"
|
|
ORPHAN_SCAN_DONE="$LOG_DIR/.orphan-scan-done"
|
|
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
# Single instance guard
|
|
if [ -f "$PIDFILE" ]; then
|
|
old_pid=$(cat "$PIDFILE")
|
|
if kill -0 "$old_pid" 2>/dev/null; then
|
|
echo "auto-commit-guard already running (PID $old_pid)" >&2
|
|
exit 0
|
|
fi
|
|
fi
|
|
echo $$ > "$PIDFILE"
|
|
trap 'rm -f "$PIDFILE"' EXIT
|
|
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] AUTO-COMMIT: $*" >> "$LOG"
|
|
}
|
|
|
|
# --- Orphaned workdir scan (runs once on startup) ---
|
|
scan_orphans() {
|
|
if [ -f "$ORPHAN_SCAN_DONE" ]; then
|
|
return 0
|
|
fi
|
|
log "Scanning /tmp for orphaned agent workdirs..."
|
|
local found=0
|
|
local rescued=0
|
|
|
|
for dir in /tmp/*-work-* /tmp/timmy-burn-* /tmp/tc-burn; do
|
|
[ -d "$dir" ] || continue
|
|
[ -d "$dir/.git" ] || continue
|
|
|
|
found=$((found + 1))
|
|
cd "$dir" 2>/dev/null || continue
|
|
|
|
local dirty
|
|
dirty=$(git status --porcelain 2>/dev/null | wc -l | tr -d " ")
|
|
if [ "${dirty:-0}" -gt 0 ]; then
|
|
local branch
|
|
branch=$(git branch --show-current 2>/dev/null || echo "orphan")
|
|
git add -A 2>/dev/null
|
|
if git commit -m "WIP: orphan rescue — $dirty file(s) auto-committed on $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
|
|
Orphaned workdir detected at $dir.
|
|
Branch: $branch
|
|
Rescued by auto-commit-guard on startup." 2>/dev/null; then
|
|
rescued=$((rescued + 1))
|
|
log "RESCUED: $dir ($dirty files on branch $branch)"
|
|
|
|
# Try to push if remote exists
|
|
if git remote get-url origin >/dev/null 2>&1; then
|
|
git push -u origin "$branch" 2>/dev/null && log "PUSHED orphan rescue: $dir → $branch" || log "PUSH FAILED orphan rescue: $dir (no remote access)"
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
|
|
log "Orphan scan complete: $found workdirs checked, $rescued rescued"
|
|
touch "$ORPHAN_SCAN_DONE"
|
|
}
|
|
|
|
# --- Main guard loop ---
|
|
guard_cycle() {
|
|
local committed=0
|
|
local scanned=0
|
|
|
|
# Scan worktree base
|
|
if [ -d "$WORKTREE_BASE" ]; then
|
|
for dir in "$WORKTREE_BASE"/*/; do
|
|
[ -d "$dir" ] || continue
|
|
[ -d "$dir/.git" ] || continue
|
|
|
|
scanned=$((scanned + 1))
|
|
cd "$dir" 2>/dev/null || continue
|
|
|
|
local dirty
|
|
dirty=$(git status --porcelain 2>/dev/null | wc -l | tr -d " ")
|
|
[ "${dirty:-0}" -eq 0 ] && continue
|
|
|
|
local branch
|
|
branch=$(git branch --show-current 2>/dev/null || echo "detached")
|
|
|
|
git add -A 2>/dev/null
|
|
if git commit -m "WIP: auto-commit — $dirty file(s) on $branch
|
|
|
|
Automated commit by auto-commit-guard at $(date -u +%Y-%m-%dT%H:%M:%SZ).
|
|
Work preserved to prevent loss on crash." 2>/dev/null; then
|
|
committed=$((committed + 1))
|
|
log "COMMITTED: $dir ($dirty files, branch $branch)"
|
|
|
|
# Push to preserve remotely
|
|
if git remote get-url origin >/dev/null 2>&1; then
|
|
git push -u origin "$branch" 2>/dev/null && log "PUSHED: $dir → $branch" || log "PUSH FAILED: $dir (will retry next cycle)"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Also scan /tmp for agent workdirs
|
|
for dir in /tmp/*-work-*; do
|
|
[ -d "$dir" ] || continue
|
|
[ -d "$dir/.git" ] || continue
|
|
|
|
scanned=$((scanned + 1))
|
|
cd "$dir" 2>/dev/null || continue
|
|
|
|
local dirty
|
|
dirty=$(git status --porcelain 2>/dev/null | wc -l | tr -d " ")
|
|
[ "${dirty:-0}" -eq 0 ] && continue
|
|
|
|
local branch
|
|
branch=$(git branch --show-current 2>/dev/null || echo "detached")
|
|
|
|
git add -A 2>/dev/null
|
|
if git commit -m "WIP: auto-commit — $dirty file(s) on $branch
|
|
|
|
Automated commit by auto-commit-guard at $(date -u +%Y-%m-%dT%H:%M:%SZ).
|
|
Agent workdir preserved to prevent loss." 2>/dev/null; then
|
|
committed=$((committed + 1))
|
|
log "COMMITTED: $dir ($dirty files, branch $branch)"
|
|
|
|
if git remote get-url origin >/dev/null 2>&1; then
|
|
git push -u origin "$branch" 2>/dev/null && log "PUSHED: $dir → $branch" || log "PUSH FAILED: $dir (will retry next cycle)"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
[ "$committed" -gt 0 ] && log "Cycle done: $scanned scanned, $committed committed"
|
|
}
|
|
|
|
# --- Entry point ---
|
|
log "Starting auto-commit-guard (interval=${INTERVAL}s, worktree=${WORKTREE_BASE})"
|
|
scan_orphans
|
|
|
|
while true; do
|
|
guard_cycle
|
|
sleep "$INTERVAL"
|
|
done
|