fix(mackup): safer defaults + recovery after broken symlink sync

- Default-off SSR_MACKUP_PRESYMLINK for DynamicProfiles pre-symlink (was
  risking empty/wrong iCloud trees and breaking iTerm2).
- Remove incorrect skip entries: built-in cfgs use different paths than
  ~/.zsh, ~/.vscode*, ~/.iterm2 — skipping hid real overlap from scan only
  but the presymlink change was the main footgun.
- Add `ssr mackup unlink` → `mackup -f link uninstall` to copy configs back
  from Mackup storage and drop symlinks (official undo).
- Docs: ssr.conf.example, README sync/restore wording, completions.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-12 20:09:23 +02:00
parent ccc1db1e93
commit 51fb589ef2
8 changed files with 71 additions and 42 deletions
+22 -21
View File
@@ -80,17 +80,6 @@ _SSR_MACKUP_SKIP=(
# Docker / VM infrastructure (large, or require special handling)
.vagrant .minikube .kube .helm
# Dot-dirs that belong to apps already covered by a Mackup built-in,
# even when the built-in covers a different path (e.g. Library/Preferences
# or Library/Application Support) rather than the dot-dir itself.
# Adding them here prevents duplicate / redundant ssr-*.cfg suggestions.
.iterm2 # iterm2.cfg covers Library/Preferences + .config/iterm2
.vscode # vscode.cfg covers Library/Application Support/Code/User
.vscode-insiders # vscode-insiders.cfg covers Library/Application Support/Code - Insiders
.zsh # zsh.cfg covers .zshrc, .zshenv, .zprofile, .zlogin, .zlogout
.bash # bash.cfg covers .bashrc, .bash_profile, .bash_logout etc.
.fish # fish.cfg covers .config/fish
# Sync-service data directories — never back up a sync engine inside iCloud
.dropbox # Dropbox data; backing up to iCloud would be circular
.OneDrive # OneDrive data directory
@@ -375,19 +364,21 @@ ssr::mackup::backup() {
[[ -n "$mackup_root" ]] && ssr::mackup::_materialize_icloud "$mackup_root"
# ---------------------------------------------------------------------------
# Pre-symlink: paths managed by apps that recreate their directory almost
# instantly after deletion, making Mackup's delete→symlink sequence lose the
# race. We pre-create the symlink → cloud BEFORE running `link install`.
# Mackup detects the existing correct symlink and skips the path entirely.
# Optional pre-symlink (OFF by default): iTerm2 can recreate
# DynamicProfiles/ so fast that `mackup link install` loses the race.
# Forcing a symlink here can break iTerm if the cloud copy is wrong or
# empty — only enable after you trust the Mackup cloud tree.
#
# export SSR_MACKUP_PRESYMLINK=1 # in ssr.conf or the shell
#
# Format: home-relative path (same as in the Mackup app definition).
# ---------------------------------------------------------------------------
local -a _presymlink=(
# iTerm2 recreates DynamicProfiles within milliseconds of deletion.
# Pre-symlinking ensures Mackup skips it; iTerm2 reads/writes through
# the symlink normally (creating new profiles goes straight to iCloud).
.config/iterm2/AppSupport/DynamicProfiles
)
local -a _presymlink=()
case "${SSR_MACKUP_PRESYMLINK:-}" in
1|true|yes|on)
_presymlink=(.config/iterm2/AppSupport/DynamicProfiles)
;;
esac
# ---------------------------------------------------------------------------
# Light preclean — remove transient directories so their ephemeral contents
@@ -510,3 +501,13 @@ ssr::mackup::restore() {
ssr::ok "mackup link completed in ${elapsed}s — symlinks created in ~/"
fi
}
# Reverse `mackup link` / `link install`: copy each managed path from the
# Mackup storage tree back into $HOME and remove the symlinks. Use when apps
# (e.g. iTerm2) misbehave after symlink-based sync or iCloud produced a bad
# tree. Invoked by `ssr mackup unlink` (interactive confirm).
ssr::mackup::link_uninstall() {
ssr::mackup::ensure_installed
ssr::log "mackup link uninstall (revert symlinks → local files)"
mackup -f link uninstall
}