#!/usr/bin/env bash
# lib/icloud.sh — iCloud Drive prefetch, wait, and keep-local helpers.
#
# All functions are no-ops when the target directory is not under iCloud
# (i.e. does not contain "Mobile Documents" in the path).
# --- Guard: only operate on iCloud paths ------------------------------------
_ssr::icloud::is_icloud() {
case "$1" in *Mobile\ Documents*) return 0 ;; esac
return 1
}
# ---------------------------------------------------------------------------
# ssr::icloud::prefetch
#
# Triggers background download of every .icloud placeholder file found under
# . Returns immediately (async). Use ssr::icloud::wait to poll for
# completion before running tools that need the real file content.
# ---------------------------------------------------------------------------
ssr::icloud::prefetch() {
local dir="$1"
[[ -d "$dir" ]] || return 0
_ssr::icloud::is_icloud "$dir" || return 0
local count=0 stub d base real
while IFS= read -r stub; do
[[ -n "$stub" ]] || continue
d="$(dirname "$stub")"
base="$(basename "$stub")"
# .icloud stubs are named ..icloud — strip leading dot + suffix.
real="${base#.}"; real="${real%.icloud}"
# open -g: open in background without bringing any app to the front.
# This is the supported way to request iCloud Drive file materialisation
# since brctl download was removed in macOS Sonoma.
open -g "$d/$real" 2>/dev/null &
count=$(( count + 1 ))
done < <(find "$dir" -name '*.icloud' -type f 2>/dev/null)
if [[ $count -gt 0 ]]; then
ssr::log "iCloud prefetch: triggered download of $count placeholder(s) in $(basename "$dir") (async)"
fi
}
# ---------------------------------------------------------------------------
# ssr::icloud::wait [timeout_seconds]
#
# Polls until all .icloud placeholder files under are gone (fully
# downloaded) or until (default 180) elapses.
# Returns 0 on full completion, 1 on timeout (non-fatal).
# ---------------------------------------------------------------------------
ssr::icloud::wait() {
local dir="$1" timeout="${2:-180}" interval=5
[[ -d "$dir" ]] || return 0
_ssr::icloud::is_icloud "$dir" || return 0
local elapsed=0 remaining
while [[ $elapsed -lt $timeout ]]; do
remaining="$(find "$dir" -name '*.icloud' -type f 2>/dev/null | wc -l | tr -d ' ')"
[[ "$remaining" -eq 0 ]] && {
ssr::ok "iCloud sync complete (all files local)"
return 0
}
ssr::log "iCloud: $remaining file(s) still downloading… (${elapsed}s/${timeout}s)"
sleep "$interval"
elapsed=$(( elapsed + interval ))
done
remaining="$(find "$dir" -name '*.icloud' -type f 2>/dev/null | wc -l | tr -d ' ')"
ssr::warn "iCloud wait timed out after ${timeout}s — $remaining placeholder(s) still pending; continuing anyway"
return 1
}
# ---------------------------------------------------------------------------
# ssr::icloud::keep_local
#
# Best-effort: marks every real (non-stub) file under as non-evictable
# so iCloud Drive does not replace them with .icloud placeholders between
# runs. Two complementary techniques:
#
# 1. Remove com.apple.icloud.evictable — iCloud-internal xattr that, when
# absent (or set to 0), prevents on-demand eviction. This xattr is
# written by iCloudDrive when eviction is allowed; removing it pins
# the file locally. (Internal API — best-effort.)
#
# 2. Update the access-time (touch -a) — iCloud uses an LRU policy; files
# accessed recently are the last to be evicted.
#
# Note: Apple provides no *public* CLI to permanently pin iCloud files.
# For a guaranteed "Keep Downloaded" equivalent the Finder UI must be used,
# or NSFileManager.setUbiquitous(false, …) from a native app.
# ---------------------------------------------------------------------------
ssr::icloud::keep_local() {
local dir="$1"
[[ -d "$dir" ]] || return 0
_ssr::icloud::is_icloud "$dir" || return 0
local count=0 f
while IFS= read -r f; do
[[ -n "$f" ]] || continue
# 1) Remove the evictable attribute (ignore errors on files that don't have it).
xattr -d "com.apple.icloud.evictable" "$f" 2>/dev/null || true
# 2) Touch access time so LRU keeps this file at the back of the eviction queue.
touch -a "$f" 2>/dev/null || true
count=$(( count + 1 ))
done < <(find "$dir" -type f -not -name '*.icloud' 2>/dev/null)
[[ $count -gt 0 ]] && ssr::log "iCloud keep-local: marked $count file(s) in $(basename "$dir") (access-time + xattr)"
return 0
}