From 554ed519c63fe7063326125c25c4ad25a2761e95 Mon Sep 17 00:00:00 2001 From: "Oliver Surke (Gitea)" Date: Mon, 18 May 2026 10:24:31 +0200 Subject: [PATCH] Enhance cloud storage handling and configuration documentation - Updated `ssr.conf.example` to clarify the usage of `SSR_CLOUD_PATH` for custom cloud providers, including examples for handling multiple folders. - Introduced `ssr::cloud::resolve_storage_pattern` function in `lib/cloud.sh` to improve directory resolution for cloud storage providers, ensuring proper handling of multiple matches. - Refactored `ssr::cloud::resolve_dir` to utilize the new storage resolution function, enhancing error handling and user feedback for cloud directory detection. --- config/ssr.conf.example | 7 +++- lib/cloud.sh | 78 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/config/ssr.conf.example b/config/ssr.conf.example index 1ad7a89..9a79e17 100644 --- a/config/ssr.conf.example +++ b/config/ssr.conf.example @@ -9,7 +9,12 @@ # Provider: icloud | dropbox | googledrive | onedrive | custom SSR_CLOUD_PROVIDER=icloud -# Only used when SSR_CLOUD_PROVIDER=custom. Absolute path. +# Absolute path. Required for SSR_CLOUD_PROVIDER=custom. +# For onedrive / googledrive / dropbox: if macOS has more than one matching +# folder under ~/Library/CloudStorage (e.g. "OneDrive" and "OneDrive - Adobe"), +# you must set SSR_CLOUD_PATH to the root you want so SSR does not pick at random. +# Example (quote if the folder name contains spaces): +# SSR_CLOUD_PATH="/Users/you/Library/CloudStorage/OneDrive - Adobe" # SSR_CLOUD_PATH=/Volumes/MyNAS/macos-sync # Subfolder inside the cloud root for SSR data: per-host backups live under diff --git a/lib/cloud.sh b/lib/cloud.sh index 36852d7..6988ce9 100644 --- a/lib/cloud.sh +++ b/lib/cloud.sh @@ -2,6 +2,66 @@ # lib/cloud.sh — cloud storage provider abstraction. # Resolves SSR_CLOUD_DIR based on SSR_CLOUD_PROVIDER from config. +# Echo one directory under ~/Library/CloudStorage matching find -name "$1". +# $2 = label for errors (e.g. OneDrive). $3 = optional fallback dir if zero matches. +# If several folders match (e.g. personal + work OneDrive), SSR_CLOUD_PATH must +# be set to the absolute path of the chosen root (same as one of the matches); +# otherwise we abort with a list of candidates. Tilde in SSR_CLOUD_PATH is expanded. +ssr::cloud::resolve_storage_pattern() { + local pattern="$1" label="$2" zero_fallback="${3:-}" + local cs="$HOME/Library/CloudStorage" + local custom="${SSR_CLOUD_PATH:-}" + case "$custom" in + '~' | '~'/*) custom="${custom/#\~/$HOME}" ;; + esac + + local matches=() + local d + if [[ -d "$cs" ]]; then + while IFS= read -r d; do + [[ -n "$d" && -d "$d" ]] && matches+=("$d") + done < <(find "$cs" -maxdepth 1 -type d -name "$pattern" 2>/dev/null | LC_ALL=C sort -u) + fi + + local n="${#matches[@]}" + if [[ "$n" -eq 0 ]]; then + if [[ -n "$zero_fallback" && -d "$zero_fallback" ]]; then + printf '%s\n' "$zero_fallback" + return 0 + fi + ssr::die "$label folder not found under ~/Library/CloudStorage" + fi + if [[ "$n" -eq 1 ]]; then + printf '%s\n' "${matches[0]}" + return 0 + fi + + if [[ -z "$custom" ]]; then + ssr::err "Multiple $label folders found; set SSR_CLOUD_PATH in ssr.conf to the root you want (absolute path), or use SSR_CLOUD_PROVIDER=custom." + for d in "${matches[@]}"; do ssr::err " $d"; done + exit 1 + fi + + ssr::validate_path "$custom" + [[ -d "$custom" ]] || ssr::die "SSR_CLOUD_PATH is not a directory: $custom" + + local abs_c abs_m picked="" + abs_c="$(cd "$custom" && pwd -P)" || ssr::die "SSR_CLOUD_PATH not accessible: $custom" + for d in "${matches[@]}"; do + abs_m="$(cd "$d" && pwd -P)" || continue + if [[ "$abs_m" == "$abs_c" ]]; then + picked="$d" + break + fi + done + if [[ -z "$picked" ]]; then + ssr::err "SSR_CLOUD_PATH=$custom is not one of the detected $label roots:" + for d in "${matches[@]}"; do ssr::err " $d"; done + exit 1 + fi + printf '%s\n' "$picked" +} + ssr::cloud::resolve_dir() { local provider="${SSR_CLOUD_PROVIDER:-icloud}" custom="${SSR_CLOUD_PATH:-}" @@ -10,23 +70,25 @@ ssr::cloud::resolve_dir() { printf '%s\n' "$HOME/Library/Mobile Documents/com~apple~CloudDocs" ;; dropbox) - if [[ -d "$HOME/Library/CloudStorage" ]]; then - find "$HOME/Library/CloudStorage" -maxdepth 1 -type d -name 'Dropbox*' -print -quit 2>/dev/null \ - || printf '%s\n' "$HOME/Dropbox" - else + if [[ ! -d "$HOME/Library/CloudStorage" ]]; then + [[ -d "$HOME/Dropbox" ]] || ssr::die "Dropbox not found (no ~/Library/CloudStorage and no ~/Dropbox)." printf '%s\n' "$HOME/Dropbox" + return 0 fi + ssr::cloud::resolve_storage_pattern 'Dropbox*' 'Dropbox' "$HOME/Dropbox" ;; googledrive) - find "$HOME/Library/CloudStorage" -maxdepth 1 -type d -name 'GoogleDrive-*' -print -quit 2>/dev/null \ - || ssr::die "Google Drive folder not found under ~/Library/CloudStorage" + ssr::cloud::resolve_storage_pattern 'GoogleDrive-*' 'Google Drive' '' ;; onedrive) - find "$HOME/Library/CloudStorage" -maxdepth 1 -type d -name 'OneDrive-*' -print -quit 2>/dev/null \ - || ssr::die "OneDrive folder not found under ~/Library/CloudStorage" + ssr::cloud::resolve_storage_pattern 'OneDrive-*' 'OneDrive' '' ;; custom) [[ -n "$custom" ]] || ssr::die "SSR_CLOUD_PROVIDER=custom requires SSR_CLOUD_PATH" + case "$custom" in + '~' | '~'/*) custom="${custom/#\~/$HOME}" ;; + esac + ssr::validate_path "$custom" printf '%s\n' "$custom" ;; *)