Commit Graph

17 Commits

Author SHA1 Message Date
Oliver Surke ccc1db1e93 chore(mackup): enhance skip list to prevent redundant configuration suggestions
Added additional dot-directories and sync-service data directories to the skip list, ensuring that paths already covered by Mackup built-ins do not generate duplicate configuration suggestions. This improves the efficiency of the backup process and reduces user confusion.
2026-05-12 20:06:43 +02:00
Oliver Surke c88e9db1c4 chore(mackup): expand skip list with installed runtimes and caches
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 17:44:16 +02:00
Oliver Surke f6b323e9f5 fix(mackup): correct built-in coverage detection and per-item scan confirm
_ssr_mackup_declared_paths:
- Fix wrong subdir: apps/ → applications/ (coverage was completely broken)
- Use Mackup's bundled Python interpreter (not system python3)
- Track xdg_configuration_files sections separately and prefix paths
  with .config/ so paths like gh/config.yml → .config/gh/config.yml
  are properly recognized as covering .config/gh

_is_declared:
- Use `command grep` to bypass colour aliases
- Replace grep -E regex (dot-escaping issues) with bash glob: == "$p/"*
- Applied to both lib/mackup.sh and bin/ssr-mackup

_SSR_MACKUP_CURATED:
- Remove entries now correctly detected as built-in covered:
  .claude (claude-code.cfg), .codex, .docker, .config/gh (github-cli xdg)
- Keep .cursor (built-in covers Library/Application Support, not .cursor/)

_SSR_MACKUP_SELECTIVE:
- Remove entries for removed curated items

ssr mackup scan:
- Confirm each new item individually (y/n per candidate)
- Confirm each stale removal individually
- Show resolved sub-paths per item before asking

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 17:41:15 +02:00
Oliver Surke 953c213ded fix: use mackup link install/link instead of backup/restore
In Mackup 0.10.2, `mackup backup` only copies files to cloud — it never
creates symlinks.  Symlinks require `mackup link install` (sync) or
`mackup link` (restore).  This is why no symlinks appeared in ~/ after
running ssr sync.

Changes:
- ssr::mackup::backup()  → now calls `mackup -f link install`
  Moves managed files to cloud and creates symlinks in ~/
  Naturally skips sockets and dangling symlinks via isfile/isdir guard,
  so the pre-cleanup is now a light transient-dir purge only.

- ssr::mackup::restore() → now calls `mackup -f link`
  Creates symlinks ~/file → cloud/file (files must already be in cloud).
  Keeps files in iCloud for ongoing sync, unlike the copy-based restore.

- Added Mackup 0.10.2 command-mapping comment to file header.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 15:37:43 +02:00
Oliver Surke 61091aa6bf refactor: ssr-*.cfg files are user-owned after creation
ssr mackup scan now only suggests NEW files — it never overwrites an
existing ssr-*.cfg. Once created, the file belongs to the user who
edits it directly in ~/.mackup/ to customise which paths are backed up.

- _ssr_mackup_write_cfg: returns early if file already exists
- scan: drops UPD logic; only shows NEW suggestions
- _ssr_mackup_resolve_paths: removed SSR_MACKUP_SELECTIVE_PATHS support
  (ssr.conf config variable removed — edit the cfg files instead)
- Scan output: hints to edit files directly after creation

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 15:06:43 +02:00
Oliver Surke be9b37010e fix: back up Docker Desktop config files only, not the whole ~/.docker dir
Docker Desktop checks that ~/.docker is a real directory and rejects it
if it is a symlink (Mackup's default when backing up a whole dir).

Solution: remove .docker from the skip list, add it to curated + selective.
Mackup will symlink only ~/.docker/config.json and ~/.docker/daemon.json —
the directory itself stays a real dir that Docker Desktop can manage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 15:03:24 +02:00
Oliver Surke 93c23c60a9 feat: selective sub-path backup for volatile dot-dirs
Mackup with engine=file_system already uses symlinks for all backed-up
apps (/Users/osurke/.docker → <cloud>/Mackup/mackup/.docker). The copy PHASE
fails when the source dir contains sockets or dangling symlinks.

For dirs like .claude that have a volatile debug/ subdir, the clean fix
is to list only the stable sub-paths in the Mackup cfg rather than the
whole directory — debug/ is never touched.

_SSR_MACKUP_SELECTIVE: built-in overrides for .claude, .cursor, .codex,
.vscode, .factory — lists only config files (settings.json, CLAUDE.md,
skills, plugins, agents, …), skipping logs/, debug/, etc.

_ssr_mackup_resolve_paths(): resolves the [configuration_files] entries
for a given item using the selective table, falling back to the whole dir.

ssr mackup scan now shows '6 sub-path(s) of .claude/' in the plan
instead of '.claude/' to make the selective behaviour visible.

Configurable via SSR_MACKUP_SELECTIVE_PATHS in ssr.conf.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 14:58:16 +02:00
Oliver Surke 3e54f0ca20 fix: pre-clean transient dirs + repeat cleanup before every mackup attempt
Root cause: Claude Code recreates ~/.claude/debug/latest (a dangling
symlink to the current debug session) almost immediately after it is
deleted. The old cleanup ran once before the retry loop, so any retry
after a 2s sleep would hit a freshly recreated symlink.

Fix: move _ssr_mackup_preclean() inside the retry loop so it runs
immediately before each mackup attempt, leaving no window for apps to
recreate problematic files.

Also remove known transient subdirectories by path before the generic
socket/dangling-link sweep:
  .claude/debug  .cursor/logs  .vscode/logs  .npm/_logs

Configurable via SSR_MACKUP_PRECLEAN_PATHS (space-separated, $HOME-relative).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 12:17:44 +02:00
Oliver Surke d849400f63 fix: skip dot-paths already declared in Mackup built-in app definitions
Previously ssr-custom cfg generation only checked whether the path was
already in the cloud backup directory.  It did not consult Mackup's
~400 built-in app definitions, causing duplicates like ssr-claude.cfg
when Mackup already has a 'claude-code' entry covering .claude.

Changes:
- _ssr_mackup_declared_paths(): parses all built-in *.cfg files from
  Mackup's Python package apps/ dir + hand-written ~/.mackup/*.cfg
  (excluding our own ssr-*.cfg) into a sorted path set
- generate_custom_cfg now skips any path already in that set
- Stale ssr-*.cfg files that now duplicate a built-in are removed
  automatically on the next sync run

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 11:49:04 +02:00
Oliver Surke b50ab61d5c fix: remove dangling symlinks before mackup backup
shutil.copy fails on dangling symlinks with [Errno 2] No such file or
directory (e.g. ~/.claude/debug/latest → missing target).

Pre-backup cleanup now handles two cases:
  - Unix sockets  (-type s)     → Errno 102
  - Dangling links (-type l !-e) → Errno 2

Both are ephemeral runtime artefacts that cannot be meaningfully backed up.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 11:00:25 +02:00
Oliver Surke 894062adcd refactor: one ~/.mackup/ssr-<name>.cfg per dot-dir instead of combined file
Each discovered dot-dir now gets its own Mackup app definition:
  .claude  → ~/.mackup/ssr-claude.cfg
  .cursor  → ~/.mackup/ssr-cursor.cfg
  .config/gh → ~/.mackup/ssr-config-gh.cfg
  …

This matches Mackup's own convention, keeps definitions independent,
and makes it easy to add/remove individual apps. The old combined
ssr-custom.cfg is removed automatically on the next sync run.

Files are written idempotently (skipped when content is unchanged).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 10:50:42 +02:00
Oliver Surke c6ac73bc83 fix: remove Unix socket files before mackup backup
Mackup fails with shutil.Error [Errno 102] when it tries to copy Unix
socket files (e.g. iTerm2's iterm2-daemon-1.socket, sockets/secrets).
Sockets are ephemeral runtime artefacts — they cannot be meaningfully
backed up and are always recreated by the owning app on startup.

Purge all -type s files from $HOME before calling mackup backup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 10:43:13 +02:00
Oliver Surke 649449c628 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 <cursoragent@cursor.com>
2026-05-12 10:05:16 +02:00
Oliver Surke cf7f61e95d feat: add progress watchdog and iCloud hints to mackup restore
- Pre-materialize iCloud stubs before restore (same as backup)
- Report stub count so user knows why it might be slow
- Background watchdog prints '… still running (Ns elapsed)' every 15s
  so long iCloud download pauses don't look like a hang
- Report total elapsed time on completion

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 22:07:42 +02:00
Oliver Surke 873cb3d51f fix: replace removed brctl download with open -g; add -f to mackup restore
brctl download was removed in macOS Sonoma — the remaining subcommands are
diagnose/log/dump/status/accounts/quota/monitor. Replace the dead call with
a find loop that uses 'open -g <real-path>' to request per-file iCloud
download from the daemon, followed by a 1-second settle pause and the
existing .icloud stub deletion.

Also add -f to mackup restore for consistency with the backup call.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 21:06:29 +02:00
Oliver Surke a4465f37c6 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>
2026-05-11 18:02:31 +02:00
Oliver Surke 539a8fc28f Initial ssr repository
Convert the legacy system_sync.sh and system_restore.sh into a modular
shareable repository:

- bin/ssr CLI dispatcher with install/sync/restore/schedule/config/status
- lib/ split: common, cloud, brew, mackup, macos
- Cloud provider abstraction (icloud, dropbox, googledrive, onedrive, custom)
- launchd template for scheduled daily sync (renderable via sed)
- ssr.conf.example with whitelisted KEY=VALUE config loader
- macos-defaults.sh extracted from inline block, overridable
- install.sh bootstrap symlinks CLI into ~/.local/bin
- README with usage, config table, migration map, security notes
- Original scripts archived under legacy/ for reference

Fixes vs originals: set -euo pipefail, quoted vars, find -print0,
spctl --global-disable (was --master-disable), MACOS=".macos" bug,
zero-width char before ln, Apple Silicon brew shellenv, config injection
hardening.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 15:05:17 +02:00