From 649449c628e820e2c146e24cc8d3b15cbda864ad Mon Sep 17 00:00:00 2001 From: "Oliver Surke (Gitea)" Date: Tue, 12 May 2026 10:05:16 +0200 Subject: [PATCH] feat: auto-generate ~/.mackup/ssr-custom.cfg for uncovered dot-dirs Before each `ssr sync`, scan $HOME for dot-dirs/files not yet present in the Mackup backup directory and write them into ~/.mackup/ssr-custom.cfg. This covers tools like .claude, .cursor, .easymcp, .codex, .factory, and any future dot-dir without requiring the user to write manual Mackup app definitions. Curated list handles common developer tools; auto-discovery adds the rest while skipping caches, credential dirs (.ssh, .gnupg, .aws, .azure) and other volatile state. Configurable via: SSR_MACKUP_AUTODISCOVER=true (opt-out with false) SSR_MACKUP_EXTRA_PATHS="..." (space-separated $HOME-relative paths) Co-authored-by: Cursor --- bin/ssr-sync | 6 +++ config/ssr.conf.example | 8 ++++ lib/mackup.sh | 103 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/bin/ssr-sync b/bin/ssr-sync index d2d8b4c..7b3f748 100755 --- a/bin/ssr-sync +++ b/bin/ssr-sync @@ -46,6 +46,12 @@ ssr::brew::upgrade # Timeout (seconds) is configurable; default 180s. ssr::icloud::wait "$mackup_dir" "${SSR_ICLOUD_WAIT_TIMEOUT:-180}" +# Auto-generate ~/.mackup/ssr-custom.cfg for dot-dirs not yet covered by +# Mackup's built-in definitions (e.g. .claude, .cursor, .easymcp, …). +if [[ "${SSR_MACKUP_AUTODISCOVER:-true}" == "true" ]]; then + ssr::mackup::generate_custom_cfg "$mackup_dir" +fi + ssr::mackup::backup ssr::brew::dump "$backup_dir" ssr::brew::cleanup diff --git a/config/ssr.conf.example b/config/ssr.conf.example index e28e0d7..3353d5b 100644 --- a/config/ssr.conf.example +++ b/config/ssr.conf.example @@ -16,6 +16,14 @@ SSR_CLOUD_PROVIDER=icloud SSR_BACKUP_SUBDIR=BACKUP SSR_MACKUP_SUBDIR=Mackup +# Auto-discover uncovered dot-dirs in $HOME and add them to a generated +# ~/.mackup/ssr-custom.cfg before each backup. Set to "false" to opt out. +SSR_MACKUP_AUTODISCOVER=true + +# Extra paths (relative to $HOME) to always include, space-separated. +# Example: ".easymcp .my-tool .config/my-cli" +# SSR_MACKUP_EXTRA_PATHS="" + # --------------------------------------------------------------------------- # Homebrew behavior during `ssr sync` # --------------------------------------------------------------------------- diff --git a/lib/mackup.sh b/lib/mackup.sh index b041fd1..705ac8a 100644 --- a/lib/mackup.sh +++ b/lib/mackup.sh @@ -4,6 +4,109 @@ # 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. +# --------------------------------------------------------------------------- +# Custom Mackup app definition auto-generator +# --------------------------------------------------------------------------- + +# Curated dot-paths that matter for developer workflows but are not in +# Mackup's built-in app definitions. Add your own via SSR_MACKUP_EXTRA_PATHS +# (space-separated) in ssr.conf. +_SSR_MACKUP_CURATED=( + .claude # Claude AI CLI config, skills, agents + .cursor # Cursor IDE config + rules + .codex # OpenAI Codex config + .easymcp # EasyMCP config + .factory # Factory AI config + .config/claude + .config/cursor + .config/gh # GitHub CLI (if not already in mackup) + .config/op # 1Password CLI config + bin # personal ~/bin scripts +) + +# Dot-dirs/files to never auto-include. +# Covers: caches, generated state, credentials, large build artefacts. +_SSR_MACKUP_SKIP=( + .Trash .localized .DS_Store .CFUserTextEncoding + .cache .npm .yarn .pnpm .bun + .gradle .m2 .ivy2 .sbt + .docker .vagrant .minikube .kube .helm + .local # runtime state, not config + .bash_history .zsh_history .zsh_sessions .sh_history + .lesshst .wget-hsts .python_history .node_repl_history + .ruby_history .irb_history .psql_history .mysql_history + .dbshell .viminfo .Xauthority + .Spotlight-V100 .fseventsd .DocumentRevisions-V100 + # Security-sensitive — back up separately with encryption + .ssh .gnupg .aws .azure .gcloud .config/gcloud +) + +# Generate (or refresh) ~/.mackup/ssr-custom.cfg with: +# 1. Curated entries that exist in $HOME and are not yet in the Mackup backup. +# 2. Any dot-dir discovered in $HOME that is not in the skip list and not +# already materialised in the Mackup backup directory. +# +# The file is picked up automatically by `mackup backup/restore`. +# Idempotent: re-running overwrites the previous version. +ssr::mackup::generate_custom_cfg() { + local mackup_dir="$1" # the cloud Mackup backup directory + local cfg_dir="$HOME/.mackup" + local cfg_file="$cfg_dir/ssr-custom.cfg" + + mkdir -p "$cfg_dir" + + # Build a fast lookup set from the skip list. + local skip_set=" ${_SSR_MACKUP_SKIP[*]} " + + # Helper: is already in the Mackup backup? + _in_backup() { [[ -e "$mackup_dir/$1" ]]; } + + local -a items=() + + # 1. Curated entries (exist on disk, not yet backed up). + local item + for item in "${_SSR_MACKUP_CURATED[@]}" ${SSR_MACKUP_EXTRA_PATHS:-}; do + [[ -e "$HOME/$item" ]] || continue + _in_backup "$item" && continue + items+=("$item") + done + + # 2. Auto-discover uncovered dot-dirs in $HOME. + local name + while IFS= read -r dotdir; do + name="${dotdir##*/}" + # Skip if in the skip set. + [[ "$skip_set" == *" $name "* ]] && continue + # Skip if already in backup. + _in_backup "$name" && continue + # Skip if already in the curated list we built above. + local dup=false + for item in "${items[@]:-}"; do [[ "$item" == "$name" ]] && dup=true && break; done + $dup && continue + items+=("$name") + done < <(find "$HOME" -maxdepth 1 -mindepth 1 -name '.*' -type d 2>/dev/null | sort) + + if [[ ${#items[@]} -eq 0 ]]; then + ssr::log "No uncovered dot-dirs to add — ~/.mackup/ssr-custom.cfg not changed." + return 0 + fi + + { + printf '# Auto-generated by ssr sync (%s)\n' "$(date '+%Y-%m-%dT%H:%M:%S')" + printf '# Do not edit manually — regenerated on every sync.\n' + printf '# Add custom paths via SSR_MACKUP_EXTRA_PATHS in ssr.conf.\n\n' + printf '[application]\n' + printf 'name = ssr-custom\n\n' + printf '[configuration_files]\n' + local it + for it in "${items[@]}"; do + printf '%s\n' "$it" + done + } > "$cfg_file" + + ssr::ok "~/.mackup/ssr-custom.cfg: ${#items[@]} item(s) added → ${items[*]}" +} + ssr::mackup::ensure_installed() { if command -v mackup >/dev/null 2>&1; then return 0