a4465f37c6
- 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>
106 lines
3.4 KiB
Bash
Executable File
106 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ssr-schedule — manage the launchd scheduled sync job.
|
|
|
|
set -euo pipefail
|
|
SSR_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
# shellcheck source=../lib/common.sh
|
|
source "$SSR_ROOT/lib/common.sh"
|
|
|
|
ssr::require_macos
|
|
ssr::load_config
|
|
|
|
LABEL="${SSR_LAUNCHD_LABEL:-de.surke.ssr.sync}"
|
|
PLIST_SRC="$SSR_ROOT/launchd/${LABEL}.plist"
|
|
PLIST_DST="$HOME/Library/LaunchAgents/${LABEL}.plist"
|
|
LOG_DIR="${SSR_LOG_DIR:-$HOME/.local/state/ssr/logs}"
|
|
INTERVAL_HOUR="${SSR_SCHEDULE_HOUR:-9}"
|
|
INTERVAL_MIN="${SSR_SCHEDULE_MINUTE:-0}"
|
|
SSR_BIN="$SSR_ROOT/bin/ssr"
|
|
|
|
# Modern launchd target. `launchctl load -w` has been deprecated since 10.11
|
|
# and is a silent no-op on recent macOS — use bootstrap/bootout against the
|
|
# GUI domain of the current user.
|
|
GUI_DOMAIN="gui/$(id -u)"
|
|
SERVICE_TARGET="${GUI_DOMAIN}/${LABEL}"
|
|
|
|
# ssr::schedule::is_loaded — exit 0 iff launchd knows the job.
|
|
# `launchctl print` is the modern check; `launchctl list <label>` is the
|
|
# legacy fallback that still works on every macOS we care about.
|
|
is_loaded() {
|
|
launchctl print "$SERVICE_TARGET" >/dev/null 2>&1 \
|
|
|| launchctl list "$LABEL" >/dev/null 2>&1
|
|
}
|
|
|
|
render_plist() {
|
|
ssr::ensure_dir "$LOG_DIR"
|
|
ssr::ensure_dir "$(dirname "$PLIST_DST")"
|
|
# Substitute placeholders from the template into the user's LaunchAgents dir.
|
|
sed \
|
|
-e "s|@@LABEL@@|${LABEL}|g" \
|
|
-e "s|@@SSR_BIN@@|${SSR_BIN}|g" \
|
|
-e "s|@@LOG_DIR@@|${LOG_DIR}|g" \
|
|
-e "s|@@HOUR@@|${INTERVAL_HOUR}|g" \
|
|
-e "s|@@MINUTE@@|${INTERVAL_MIN}|g" \
|
|
"$PLIST_SRC" > "$PLIST_DST"
|
|
chmod 600 "$PLIST_DST"
|
|
}
|
|
|
|
# Boot the agent into the user's GUI domain, captures stderr for diagnostics.
|
|
# Falls back to the legacy `load -w` only if bootstrap fails entirely.
|
|
load_agent() {
|
|
local err
|
|
# Always bootout first so re-runs pick up plist changes.
|
|
launchctl bootout "$SERVICE_TARGET" >/dev/null 2>&1 || true
|
|
|
|
err="$(launchctl bootstrap "$GUI_DOMAIN" "$PLIST_DST" 2>&1)" || {
|
|
ssr::warn "launchctl bootstrap failed: ${err:-no stderr}"
|
|
ssr::warn "Falling back to legacy 'launchctl load -w'"
|
|
launchctl load -w "$PLIST_DST" 2>&1 || true
|
|
}
|
|
# `enable` makes the job survive logout/reboot (parity with old `load -w`).
|
|
launchctl enable "$SERVICE_TARGET" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
unload_agent() {
|
|
launchctl bootout "$SERVICE_TARGET" >/dev/null 2>&1 \
|
|
|| launchctl unload "$PLIST_DST" 2>/dev/null || true
|
|
}
|
|
|
|
cmd_on() {
|
|
[[ -f "$PLIST_SRC" ]] || ssr::die "launchd template missing: $PLIST_SRC"
|
|
render_plist
|
|
load_agent
|
|
if is_loaded; then
|
|
ssr::ok "Scheduled sync enabled (${INTERVAL_HOUR}:$(printf '%02d' "$INTERVAL_MIN") daily) → $PLIST_DST"
|
|
else
|
|
ssr::die "launchd did not register the job. Diagnose with:
|
|
launchctl bootstrap $GUI_DOMAIN $PLIST_DST
|
|
launchctl print $SERVICE_TARGET"
|
|
fi
|
|
}
|
|
|
|
cmd_off() {
|
|
if [[ -f "$PLIST_DST" ]]; then
|
|
unload_agent
|
|
rm -f "$PLIST_DST"
|
|
ssr::ok "Scheduled sync disabled"
|
|
else
|
|
ssr::log "Scheduled sync was not installed"
|
|
fi
|
|
}
|
|
|
|
cmd_status() {
|
|
if is_loaded; then
|
|
ssr::ok "Scheduled sync: active (label=$LABEL)"
|
|
else
|
|
ssr::log "Scheduled sync: inactive"
|
|
fi
|
|
}
|
|
|
|
case "${1:-status}" in
|
|
on|enable) cmd_on ;;
|
|
off|disable) cmd_off ;;
|
|
status) cmd_status ;;
|
|
*) ssr::die "Usage: ssr schedule [on|off|status]" ;;
|
|
esac
|