diff --git a/config/ssr.conf.example b/config/ssr.conf.example index 9d7cdaa..59249bb 100644 --- a/config/ssr.conf.example +++ b/config/ssr.conf.example @@ -20,6 +20,12 @@ SSR_MACKUP_SUBDIR=Mackup # ~/.mackup/ssr-custom.cfg before each backup. Set to "false" to opt out. SSR_MACKUP_AUTODISCOVER=true +# Subdirs (relative to $HOME) to delete before each mackup backup attempt. +# Handles transient dirs with dangling symlinks or sockets (debug sessions, +# log dirs) that shutil.copytree cannot copy. +# Built-in defaults: .claude/debug .cursor/logs .vscode/logs .npm/_logs +# SSR_MACKUP_PRECLEAN_PATHS=".my-app/tmp .other-app/run" + # Extra paths (relative to $HOME) to always include, space-separated. # Example: ".easymcp .my-tool .config/my-cli" # SSR_MACKUP_EXTRA_PATHS="" diff --git a/lib/mackup.sh b/lib/mackup.sh index 5a28ce9..01584c7 100644 --- a/lib/mackup.sh +++ b/lib/mackup.sh @@ -262,28 +262,48 @@ ssr::mackup::backup() { fi [[ -n "$mackup_root" ]] && ssr::mackup::_materialize_icloud "$mackup_root" - # Pre-backup cleanup: remove file types that shutil.copy cannot handle. - # - # 1. Unix socket files (-type s) — runtime artefacts, recreated on app start. - # Cause: shutil.Error [Errno 102] Operation not supported on socket - local socket_count - socket_count="$(find "$HOME" -type s 2>/dev/null | wc -l | tr -d ' ')" - if [[ "$socket_count" -gt 0 ]]; then - ssr::log "Removing $socket_count Unix socket file(s) before mackup backup (runtime artefacts)" - find "$HOME" -type s -delete 2>/dev/null || true - fi + # --------------------------------------------------------------------------- + # Pre-backup cleanup — remove artefacts that shutil.copytree cannot handle. + # Run this immediately before each mackup attempt so that apps (e.g. Claude + # Code) that recreate debug symlinks very quickly don't sneak back in. + # --------------------------------------------------------------------------- + _ssr_mackup_preclean() { + # Known transient subdirectories that apps recreate constantly. + # These contain debug logs / sockets, never real config. + # Extend via SSR_MACKUP_PRECLEAN_PATHS (space-separated $HOME-relative). + local -a transient_dirs=( + .claude/debug # Claude Code debug session links + .cursor/logs # Cursor IDE logs + .vscode/logs # VS Code logs + .vscode-insiders/logs + .npm/_logs # npm debug logs + ) + local extra dir + for extra in ${SSR_MACKUP_PRECLEAN_PATHS:-}; do + transient_dirs+=("$extra") + done + for dir in "${transient_dirs[@]}"; do + [[ -e "$HOME/$dir" ]] && rm -rf "$HOME/$dir" 2>/dev/null || true + done - # 2. Dangling symlinks (-type l ! -e) — point to a non-existent target. - # Cause: shutil.Error [Errno 2] No such file or directory: '' - local dangling_count - dangling_count="$(find "$HOME" -type l ! -e 2>/dev/null | wc -l | tr -d ' ')" - if [[ "$dangling_count" -gt 0 ]]; then - ssr::log "Removing $dangling_count dangling symlink(s) before mackup backup" - find "$HOME" -type l ! -e -delete 2>/dev/null || true - fi + # 1. Unix socket files (-type s) — [Errno 102] Operation not supported. + local n; n="$(find "$HOME" -type s 2>/dev/null | wc -l | tr -d ' ')" + if [[ "$n" -gt 0 ]]; then + ssr::log " pre-clean: removing $n socket file(s)" + find "$HOME" -type s -delete 2>/dev/null || true + fi + + # 2. Dangling symlinks (-type l ! -e) — [Errno 2] No such file. + n="$(find "$HOME" -type l ! -e 2>/dev/null | wc -l | tr -d ' ')" + if [[ "$n" -gt 0 ]]; then + ssr::log " pre-clean: removing $n dangling symlink(s)" + find "$HOME" -type l ! -e -delete 2>/dev/null || true + fi + } local attempt rc=0 for attempt in 1 2 3; do + _ssr_mackup_preclean # clean immediately before each attempt if mackup -f backup; then return 0 fi