Improve schedule and mackup reliability
- bin/ssr-schedule: use `launchctl bootstrap`/`bootout` (modern API) with legacy `load -w` fallback. The previous `load -w`-only flow silently no-op'd on macOS 10.15+ in some configurations, leaving `ssr status` reporting `inactive` after a successful `ssr schedule on`. - bin/ssr-status: query the service via `launchctl print`/`list <label>` for consistent active/inactive detection. - lib/mackup.sh: pre-materialize iCloud placeholders via `brctl download` and retry `mackup -f backup` up to three times to dodge the recurring `OSError [Errno 66] Directory not empty` race between mackup's rmtree and iCloud's filesystem provider. Soft-fails so the rest of `ssr sync` still runs even if mackup never settles; logs actionable hints. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+55
-1
@@ -32,9 +32,63 @@ ssr::mackup::configure() {
|
||||
ssr::ok "Wrote $cfg"
|
||||
}
|
||||
|
||||
# Force iCloud Drive to materialize placeholder (.icloud) files under the
|
||||
# Mackup storage directory before mackup walks them. The most common ssr sync
|
||||
# crash is `OSError [Errno 66] Directory not empty` raised by shutil.rmtree
|
||||
# inside mackup when iCloud lazily re-creates dataless placeholders between
|
||||
# listdir() and rmdir(). Pre-downloading sidesteps the race.
|
||||
ssr::mackup::_materialize_icloud() {
|
||||
local mackup_root="$1"
|
||||
[[ -d "$mackup_root" ]] || return 0
|
||||
case "$mackup_root" in
|
||||
*Mobile\ Documents*) ;; # only worth doing for iCloud
|
||||
*) return 0 ;;
|
||||
esac
|
||||
# `brctl download` is the documented way to request file materialization.
|
||||
# Available on macOS 10.12+; silently skip if missing on older systems.
|
||||
if command -v brctl >/dev/null 2>&1; then
|
||||
brctl download "$mackup_root" >/dev/null 2>&1 || true
|
||||
fi
|
||||
# Belt-and-braces: prune any stub files that survived the download
|
||||
# request (sometimes left over from interrupted prior runs).
|
||||
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.
|
||||
ssr::mackup::backup() {
|
||||
ssr::log "mackup backup"
|
||||
mackup -f backup
|
||||
|
||||
# Derive mackup root from ~/.mackup.cfg (path + directory).
|
||||
local cfg="$HOME/.mackup.cfg" mackup_root="" path dir
|
||||
if [[ -f "$cfg" ]]; then
|
||||
path="$(awk -F= '/^path/{gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2); print $2}' "$cfg")"
|
||||
dir="$( awk -F= '/^directory/{gsub(/^[[:space:]]+|[[:space:]]+$/,"",$2); print $2}' "$cfg")"
|
||||
[[ -n "$path" && -n "$dir" ]] && mackup_root="$path/$dir"
|
||||
fi
|
||||
[[ -n "$mackup_root" ]] && ssr::mackup::_materialize_icloud "$mackup_root"
|
||||
|
||||
local attempt rc=0
|
||||
for attempt in 1 2 3; do
|
||||
if mackup -f backup; then
|
||||
return 0
|
||||
fi
|
||||
rc=$?
|
||||
ssr::warn "mackup backup 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)"
|
||||
return 0 # soft-fail so the rest of ssr sync still runs
|
||||
}
|
||||
|
||||
ssr::mackup::restore() {
|
||||
|
||||
Reference in New Issue
Block a user