fix: use mackup link install/link instead of backup/restore
In Mackup 0.10.2, `mackup backup` only copies files to cloud — it never creates symlinks. Symlinks require `mackup link install` (sync) or `mackup link` (restore). This is why no symlinks appeared in ~/ after running ssr sync. Changes: - ssr::mackup::backup() → now calls `mackup -f link install` Moves managed files to cloud and creates symlinks in ~/ Naturally skips sockets and dangling symlinks via isfile/isdir guard, so the pre-cleanup is now a light transient-dir purge only. - ssr::mackup::restore() → now calls `mackup -f link` Creates symlinks ~/file → cloud/file (files must already be in cloud). Keeps files in iCloud for ongoing sync, unlike the copy-based restore. - Added Mackup 0.10.2 command-mapping comment to file header. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+48
-49
@@ -3,6 +3,12 @@
|
||||
# Note: upstream mackup is in low-maintenance mode; we keep it because it
|
||||
# remains the simplest way to sync dotfiles for a wide range of apps. The
|
||||
# user can swap engines via ~/.mackup.cfg without changing this script.
|
||||
#
|
||||
# Mackup 0.10.2 command mapping (importantly different from older versions):
|
||||
# mackup backup → copy only; NO symlinks created in ~/
|
||||
# mackup restore → copy only; NO symlinks created in ~/
|
||||
# mackup link install → copy to cloud + create symlinks in ~/ ← SSR uses this
|
||||
# mackup link → create symlinks in ~/ (cloud files must exist) ← SSR restore
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Custom Mackup app definition auto-generator
|
||||
@@ -292,13 +298,21 @@ ssr::mackup::_materialize_icloud() {
|
||||
find "$mackup_root" -name '*.icloud' -type f -delete 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Try `mackup -f backup` with a small retry budget. iCloud-induced races are
|
||||
# transient: a 2-second sleep is usually enough for the provider to settle.
|
||||
# We deliberately do NOT abort `ssr sync` on persistent failure — the user
|
||||
# still benefits from brew dump, cleanup, and the unmanaged-apps scan that
|
||||
# run afterwards. The failure is surfaced loudly via ssr::warn and syslog.
|
||||
# Run `mackup -f link install` — this is the correct "backup with symlinks"
|
||||
# command in Mackup 0.10.2. Unlike `mackup backup` (which only copies),
|
||||
# `link install` moves each managed file to the cloud folder and replaces it
|
||||
# with a symlink. On subsequent runs it detects already-linked files and
|
||||
# skips them efficiently.
|
||||
#
|
||||
# Sockets and dangling symlinks are naturally skipped by `link install` because
|
||||
# Python's os.path.isfile/isdir returns False for those types. We still run a
|
||||
# light preclean for known transient directories so their contents don't appear
|
||||
# as stale files in the cloud copy.
|
||||
#
|
||||
# We do NOT abort `ssr sync` on persistent failure — brew dump, cleanup, and
|
||||
# the unmanaged-apps scan still run. Failure is surfaced via ssr::warn.
|
||||
ssr::mackup::backup() {
|
||||
ssr::log "mackup backup"
|
||||
ssr::log "mackup link install (backup + symlink)"
|
||||
|
||||
# Derive mackup root from ~/.mackup.cfg (path + directory).
|
||||
local cfg="$HOME/.mackup.cfg" mackup_root="" path dir
|
||||
@@ -310,14 +324,12 @@ ssr::mackup::backup() {
|
||||
[[ -n "$mackup_root" ]] && ssr::mackup::_materialize_icloud "$mackup_root"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 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.
|
||||
# Light preclean — remove transient directories so their ephemeral contents
|
||||
# (debug symlinks, log files) are not included in the cloud copy.
|
||||
# `link install` already skips sockets and dangling symlinks via its own
|
||||
# os.path.isfile/isdir guard, so we only need to handle known directories.
|
||||
# ---------------------------------------------------------------------------
|
||||
_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
|
||||
@@ -325,55 +337,42 @@ ssr::mackup::backup() {
|
||||
.vscode-insiders/logs
|
||||
.npm/_logs # npm debug logs
|
||||
)
|
||||
local extra dir
|
||||
local extra d
|
||||
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
|
||||
for d in "${transient_dirs[@]}"; do
|
||||
[[ -e "$HOME/$d" ]] && rm -rf "${HOME:?}/$d" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# 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
|
||||
_ssr_mackup_preclean
|
||||
if mackup -f link install; then
|
||||
ssr::ok "mackup link install complete — symlinks created in ~/"
|
||||
return 0
|
||||
fi
|
||||
rc=$?
|
||||
ssr::warn "mackup backup failed (attempt $attempt/3, rc=$rc)"
|
||||
ssr::warn "mackup link install failed (attempt $attempt/3, rc=$rc)"
|
||||
if [[ $attempt -lt 3 ]]; then
|
||||
[[ -n "$mackup_root" ]] && ssr::mackup::_materialize_icloud "$mackup_root"
|
||||
sleep 2
|
||||
fi
|
||||
done
|
||||
|
||||
ssr::warn "mackup backup did not complete after 3 attempts (rc=$rc)."
|
||||
ssr::warn "Sync will continue without it; rerun 'mackup -f backup' manually,"
|
||||
ssr::warn "or switch ~/.mackup.cfg to a non-iCloud storage path if this persists."
|
||||
ssr::syslog "mackup backup failed after retries (rc=$rc)"
|
||||
ssr::warn "mackup link install did not complete after 3 attempts (rc=$rc)."
|
||||
ssr::warn "Sync will continue without it; run 'mackup -f link install' manually,"
|
||||
ssr::warn "or check ~/.mackup.cfg if this persists."
|
||||
ssr::syslog "mackup link install failed after retries (rc=$rc)"
|
||||
return 0 # soft-fail so the rest of ssr sync still runs
|
||||
}
|
||||
|
||||
ssr::mackup::restore() {
|
||||
ssr::log "mackup restore"
|
||||
ssr::log "mackup link (restore via symlinks)"
|
||||
|
||||
# Pre-materialize iCloud placeholders so mackup doesn't have to wait for
|
||||
# each file to download one-by-one during the restore walk.
|
||||
# Pre-materialize iCloud placeholders so mackup's isfile/isdir checks
|
||||
# see real files rather than .icloud stubs (which read as non-existent).
|
||||
local cfg="$HOME/.mackup.cfg" mackup_root="" path dir
|
||||
if [[ -f "$cfg" ]]; then
|
||||
path="$(awk -F= '/^path/{gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2); print $2}' "$cfg")"
|
||||
@@ -383,7 +382,6 @@ ssr::mackup::restore() {
|
||||
if [[ -n "$mackup_root" ]]; then
|
||||
ssr::log "Materializing iCloud files in $mackup_root (may take a moment)…"
|
||||
ssr::mackup::_materialize_icloud "$mackup_root"
|
||||
# Count remaining stubs; if there are many, give iCloud more time.
|
||||
local stub_count
|
||||
stub_count="$(find "$mackup_root" -name '*.icloud' -type f 2>/dev/null | wc -l | tr -d ' ')"
|
||||
if [[ "$stub_count" -gt 0 ]]; then
|
||||
@@ -395,12 +393,11 @@ ssr::mackup::restore() {
|
||||
local stub_total
|
||||
stub_total="$(find "${mackup_root:-/dev/null}" -name '*.icloud' -type f 2>/dev/null | wc -l | tr -d ' ')"
|
||||
if [[ "$stub_total" -gt 0 ]]; then
|
||||
ssr::warn "$stub_total file(s) still need downloading from iCloud — restore may pause between files."
|
||||
ssr::warn "$stub_total file(s) still need downloading — symlinks may be skipped for those."
|
||||
fi
|
||||
ssr::log "Running mackup restore (long pauses are normal while iCloud fetches files)…"
|
||||
ssr::log "Running mackup link (long pauses normal while iCloud fetches files)…"
|
||||
|
||||
# Background watchdog: prints elapsed time every 15s so the user knows the
|
||||
# process is still alive during iCloud download pauses.
|
||||
# Background watchdog: prints elapsed time every 15s.
|
||||
local start_ts rc=0 done_flag watchdog_pid
|
||||
start_ts="$(date +%s)"
|
||||
done_flag="$(mktemp)"
|
||||
@@ -408,14 +405,16 @@ ssr::mackup::restore() {
|
||||
while [[ -f "$done_flag" ]]; do
|
||||
sleep 15
|
||||
[[ -f "$done_flag" ]] || break
|
||||
printf ' … mackup restore still running (%ds elapsed — waiting for iCloud)\n' \
|
||||
printf ' … mackup link still running (%ds elapsed — waiting for iCloud)\n' \
|
||||
"$(( $(date +%s) - start_ts ))" >&2
|
||||
done
|
||||
) &
|
||||
watchdog_pid=$!
|
||||
|
||||
# -f: answer "yes" to all prompts (consistent with backup behaviour).
|
||||
mackup -f restore || rc=$?
|
||||
# `mackup link` creates symlinks ~/file → cloud/file for all managed apps.
|
||||
# Unlike `mackup restore` (which copies), this keeps files in iCloud and
|
||||
# ensures ongoing sync. -f auto-confirms replacement of existing home files.
|
||||
mackup -f link || rc=$?
|
||||
|
||||
rm -f "$done_flag"
|
||||
kill "$watchdog_pid" 2>/dev/null || true
|
||||
@@ -423,8 +422,8 @@ ssr::mackup::restore() {
|
||||
|
||||
local elapsed=$(( $(date +%s) - start_ts ))
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
ssr::warn "mackup restore finished with errors (rc=$rc, ${elapsed}s elapsed)"
|
||||
ssr::warn "mackup link finished with errors (rc=$rc, ${elapsed}s elapsed)"
|
||||
else
|
||||
ssr::ok "mackup restore completed in ${elapsed}s"
|
||||
ssr::ok "mackup link completed in ${elapsed}s — symlinks created in ~/"
|
||||
fi
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user