|
#!/usr/bin/env bash |
|
# backup-precheck.sh — /config Disk Usage Analysis & Cleanup |
|
# Usage: backup-precheck.sh [--clean] [--aggressive] [--dry-run] |
|
set -eo pipefail |
|
|
|
CONFIG="/config" |
|
DRY_RUN=false |
|
CLEAN=false |
|
AGGRESSIVE=false |
|
|
|
for arg in "$@"; do |
|
case "$arg" in |
|
--dry-run) DRY_RUN=true ;; |
|
--clean) CLEAN=true ;; |
|
--aggressive) AGGRESSIVE=true ;; |
|
--help|-h) |
|
echo "Usage: backup-precheck.sh [--clean] [--aggressive] [--dry-run]" |
|
echo " (no args) Read-only disk analysis" |
|
echo " --clean Safe cleanup (caches, backup rotation, old logs)" |
|
echo " --aggressive Independently purge puppeteer/chromium cache + legacy archives" |
|
echo " --dry-run Preview what would be deleted (works with --clean and/or --aggressive)" |
|
exit 0 ;; |
|
esac |
|
done |
|
|
|
# safe size helpers — filter stderr noise, always return clean output |
|
du_b() { |
|
local b |
|
b=$(du -sb "$1" 2>&1 | grep -E '^[0-9]' | cut -f1 | head -1) |
|
echo "${b:-0}" |
|
} |
|
|
|
du_h() { |
|
local h |
|
h=$(du -sh "$1" 2>&1 | grep -E '^[0-9.]' | cut -f1 | head -1) |
|
echo "${h:-?}" |
|
} |
|
|
|
hr_size() { |
|
local bytes=${1:-0} |
|
[ "$bytes" -lt 0 ] 2>/dev/null && bytes=0 |
|
if [ "$bytes" -ge 1073741824 ]; then |
|
echo "$(echo "scale=1; $bytes/1073741824" | bc)G" |
|
elif [ "$bytes" -ge 1048576 ]; then |
|
echo "$(echo "scale=1; $bytes/1048576" | bc)M" |
|
elif [ "$bytes" -ge 1024 ]; then |
|
echo "$(echo "scale=1; $bytes/1024" | bc)K" |
|
else |
|
echo "${bytes}B" |
|
fi |
|
} |
|
|
|
echo "══════════════════════════════════════════" |
|
echo " backup-precheck — $(date)" |
|
echo "══════════════════════════════════════════" |
|
|
|
TOTAL=$(du_b "$CONFIG") |
|
echo " /config: $(hr_size $TOTAL)" |
|
df -h "$CONFIG" 2>/dev/null | tail -1 | awk '{printf " FS: %s total, %s used, %s avail (%s)\n", $2, $3, $4, $5}' |
|
|
|
echo "" |
|
echo "=== Visible Folders ===" |
|
du -sh "$CONFIG"/*/ 2>/dev/null | sort -rh | head -15 || true |
|
|
|
echo "" |
|
echo "=== Hidden Directories ===" |
|
for d in "$CONFIG"/.*/; do |
|
base=$(basename "$d") |
|
[ "$base" = "." ] || [ "$base" = ".." ] && continue |
|
[ -d "$d" ] || continue |
|
sz=$(du_h "$d") |
|
printf " %s %s\n" "$sz" "$base" |
|
done | sort -rh || true |
|
|
|
echo "" |
|
echo "=== Large Files (>1MB, top-level) ===" |
|
find "$CONFIG" -maxdepth 1 -type f -size +1M 2>/dev/null | while read -r f; do |
|
printf " %s %s\n" "$(du_h "$f")" "$(basename "$f")" |
|
done || true |
|
|
|
# ── Deletion candidates ── |
|
echo "" |
|
echo "=== Deletion Candidates ===" |
|
|
|
DB_BACKUP_DIR="$CONFIG/.omniroute/db_backups" |
|
if [ -d "$DB_BACKUP_DIR" ]; then |
|
BK_COUNT=$(find "$DB_BACKUP_DIR" -name '*.sqlite' 2>/dev/null | wc -l) || BK_COUNT=0 |
|
BK_SIZE=$(du_h "$DB_BACKUP_DIR") |
|
echo " 🟡 OmniRoute DB backups: $BK_SIZE ($BK_COUNT files)" |
|
if [ "$BK_COUNT" -gt 3 ]; then |
|
newest3=$(ls -t "$DB_BACKUP_DIR"/*.sqlite 2>/dev/null | head -3 | tail -1) || true |
|
if [ -n "$newest3" ] && [ -f "$newest3" ]; then |
|
BK_KEEP_B=$(du_b "$newest3") |
|
BK_TOTAL_B=$(du_b "$DB_BACKUP_DIR") |
|
BK_RECLAIM=$(( BK_TOTAL_B - BK_KEEP_B * 3 )) |
|
echo " → Keep 3 newest, reclaim ~$(hr_size $BK_RECLAIM)" |
|
fi |
|
fi |
|
fi |
|
|
|
for c in "$CONFIG/.cache/pip" "$CONFIG/.cache/uv" "$CONFIG/.cache/typescript"; do |
|
[ -d "$c" ] || continue |
|
echo " 🔵 $(basename $c) cache: $(du_h "$c") (safe to purge)" |
|
done |
|
|
|
[ -d "$CONFIG/.cache/puppeteer" ] && \ |
|
echo " ⚠️ Puppeteer cache: $(du_h "$CONFIG/.cache/puppeteer") (re-downloads on next use)" |
|
|
|
[ -d "$CONFIG/.cache/chromium" ] && \ |
|
echo " ⚠️ Chromium cache: $(du_h "$CONFIG/.cache/chromium") (browser cache, auto-rebuilds)" |
|
|
|
[ -d "$CONFIG/.omniroute/call_logs" ] && \ |
|
echo " 🔵 OmniRoute call logs: $(du_h "$CONFIG/.omniroute/call_logs") (clean old >30d)" |
|
|
|
[ -f "$CONFIG/Documents/legacy_openclaw.zip" ] && \ |
|
echo " ❓ Legacy archive: $(du_h "$CONFIG/Documents/legacy_openclaw.zip") (review before delete)" |
|
|
|
# ── Reclaim estimate (all arithmetic via intermediate vars) ── |
|
echo "" |
|
echo "=== Estimated Reclaimable ===" |
|
SAFE_R=0 |
|
for d in "$CONFIG/.cache/pip" "$CONFIG/.cache/uv" "$CONFIG/.cache/typescript"; do |
|
[ -d "$d" ] || continue |
|
b=$(du_b "$d") |
|
SAFE_R=$(( SAFE_R + b )) |
|
done |
|
if [ -d "$DB_BACKUP_DIR" ]; then |
|
BC=$(find "$DB_BACKUP_DIR" -name '*.sqlite' | wc -l) || BC=0 |
|
if [ "$BC" -gt 3 ]; then |
|
n3=$(ls -t "$DB_BACKUP_DIR"/*.sqlite 2>/dev/null | head -3 | tail -1) || true |
|
if [ -n "$n3" ] && [ -f "$n3" ]; then |
|
b_total=$(du_b "$DB_BACKUP_DIR") |
|
b_one=$(du_b "$n3") |
|
SAFE_R=$(( SAFE_R + b_total - b_one * 3 )) |
|
fi |
|
fi |
|
fi |
|
|
|
echo " Safe (--clean): $(hr_size $SAFE_R)" |
|
echo " After safe clean: $(hr_size $(( TOTAL - SAFE_R )))" |
|
|
|
AGGR_R=$SAFE_R |
|
for d in "$CONFIG/.cache/puppeteer" "$CONFIG/.cache/chromium"; do |
|
[ -d "$d" ] || continue |
|
b=$(du_b "$d") |
|
AGGR_R=$(( AGGR_R + b )) |
|
done |
|
[ -f "$CONFIG/Documents/legacy_openclaw.zip" ] && AGGR_R=$(( AGGR_R + $(du_b "$CONFIG/Documents/legacy_openclaw.zip") )) |
|
|
|
if [ "$AGGR_R" -gt "$SAFE_R" ]; then |
|
echo " +Aggressive extras: $(hr_size $(( AGGR_R - SAFE_R )))" |
|
echo " After all cleanup: $(hr_size $(( TOTAL - AGGR_R )))" |
|
fi |
|
|
|
# ── Execute cleanup ── |
|
# Helper: purge a cache directory if it exists (defined early so both --clean and --aggressive can use it) |
|
clean_dir() { |
|
label=$1; dir=$2 |
|
[ -d "$dir" ] || return 0 |
|
sz=$(du_h "$dir") |
|
if $DRY_RUN; then |
|
echo " [dry-run] Purge $label ($sz)" |
|
else |
|
rm -rf "$dir" 2>/dev/null || true |
|
echo " ✅ Purged $label ($sz → 0)" |
|
fi |
|
} |
|
|
|
if $CLEAN; then |
|
echo "" |
|
echo "=== Executing Cleanup ===" |
|
|
|
clean_dir "pip cache" "$CONFIG/.cache/pip" |
|
clean_dir "uv cache" "$CONFIG/.cache/uv" |
|
clean_dir "TypeScript cache" "$CONFIG/.cache/typescript" |
|
|
|
# DB backups: keep 3 |
|
if [ -d "$DB_BACKUP_DIR" ]; then |
|
BC=$(find "$DB_BACKUP_DIR" -name '*.sqlite' | wc -l) || BC=0 |
|
if [ "$BC" -gt 3 ]; then |
|
sz_before=$(du_h "$DB_BACKUP_DIR") |
|
if $DRY_RUN; then |
|
echo " [dry-run] Prune $(( BC - 3 )) old DB backups ($sz_before → keep 3)" |
|
else |
|
ls -t "$DB_BACKUP_DIR"/*.sqlite 2>/dev/null | tail -n +4 | while read -r old; do |
|
rm -f "$old" "${old}-journal" "${old}-wal" "${old}-shm" 2>/dev/null || true |
|
done |
|
echo " ✅ Pruned DB backups ($sz_before → $(du_h "$DB_BACKUP_DIR"))" |
|
fi |
|
fi |
|
fi |
|
|
|
# Call logs >30 days |
|
if [ -d "$CONFIG/.omniroute/call_logs" ]; then |
|
find "$CONFIG/.omniroute/call_logs" -type f -name "????-??-??" 2>/dev/null | while read -r f; do |
|
fname=$(basename "$f") |
|
fepoch=$(date -d "$fname" +%s 2>/dev/null || echo 0) |
|
[ "$fepoch" -eq 0 ] && continue |
|
now_epoch=$(date +%s) |
|
[ $(( (now_epoch - fepoch) / 86400 )) -le 30 ] && continue |
|
if $DRY_RUN; then |
|
echo " [dry-run] Remove old call log: $fname ($(du_h "$f"))" |
|
else |
|
rm -f "$f" |
|
echo " ✅ Removed old call log: $fname" |
|
fi |
|
done || true |
|
fi |
|
|
|
# Old Hermes log rotas (agent.log.2+) |
|
for lf in "$CONFIG/.hermes/logs"/agent.log.*; do |
|
[ -f "$lf" ] || continue |
|
idx="${lf##*.}" |
|
[ "$idx" -ge 2 ] 2>/dev/null || continue |
|
sz=$(du_h "$lf") |
|
if $DRY_RUN; then |
|
echo " [dry-run] Remove old log: $(basename "$lf") ($sz)" |
|
else |
|
rm -f "$lf" |
|
echo " ✅ Removed old log: $(basename "$lf") ($sz)" |
|
fi |
|
done |
|
fi |
|
|
|
# Aggressive extras (independent of --clean) |
|
if $AGGRESSIVE; then |
|
clean_dir "Puppeteer cache" "$CONFIG/.cache/puppeteer" |
|
clean_dir "Chromium cache" "$CONFIG/.cache/chromium" |
|
if [ -f "$CONFIG/Documents/legacy_openclaw.zip" ]; then |
|
sz=$(du_h "$CONFIG/Documents/legacy_openclaw.zip") |
|
if $DRY_RUN; then |
|
echo " [dry-run] Delete legacy_openclaw.zip ($sz)" |
|
else |
|
rm -f "$CONFIG/Documents/legacy_openclaw.zip" |
|
echo " ✅ Deleted legacy_openclaw.zip ($sz)" |
|
fi |
|
fi |
|
fi |
|
|
|
# ── Final ── |
|
echo "" |
|
echo "══════════════════════════════════════════" |
|
echo " /config final: $(du_h "$CONFIG")" |
|
echo " Done — backup-precheck.sh --help" |
|
echo "══════════════════════════════════════════" |