From 539a8fc28f67e397477bd819990ae2399b5c73b4 Mon Sep 17 00:00:00 2001 From: "Oliver Surke (Gitea)" Date: Mon, 11 May 2026 15:05:17 +0200 Subject: [PATCH] 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 --- .gitignore | 18 +++ LICENSE | 21 +++ README.md | 220 ++++++++++++++++++++++++++++++++ VERSION | 1 + bin/ssr | 73 +++++++++++ bin/ssr-config | 36 ++++++ bin/ssr-install | 47 +++++++ bin/ssr-restore | 57 +++++++++ bin/ssr-schedule | 66 ++++++++++ bin/ssr-status | 38 ++++++ bin/ssr-sync | 43 +++++++ config/macos-defaults.sh | 45 +++++++ config/ssr.conf.example | 49 +++++++ install.sh | 8 ++ launchd/de.surke.ssr.sync.plist | 44 +++++++ legacy/README.md | 9 ++ legacy/system_restore.sh | 77 +++++++++++ legacy/system_sync.sh | 27 ++++ lib/brew.sh | 58 +++++++++ lib/cloud.sh | 63 +++++++++ lib/common.sh | 82 ++++++++++++ lib/mackup.sh | 43 +++++++ lib/macos.sh | 46 +++++++ 23 files changed, 1171 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 VERSION create mode 100755 bin/ssr create mode 100755 bin/ssr-config create mode 100755 bin/ssr-install create mode 100755 bin/ssr-restore create mode 100755 bin/ssr-schedule create mode 100755 bin/ssr-status create mode 100755 bin/ssr-sync create mode 100755 config/macos-defaults.sh create mode 100644 config/ssr.conf.example create mode 100755 install.sh create mode 100644 launchd/de.surke.ssr.sync.plist create mode 100644 legacy/README.md create mode 100755 legacy/system_restore.sh create mode 100755 legacy/system_sync.sh create mode 100644 lib/brew.sh create mode 100644 lib/cloud.sh create mode 100644 lib/common.sh create mode 100644 lib/mackup.sh create mode 100644 lib/macos.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e44544 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# macOS noise +.DS_Store +._* +.Spotlight-V100 +.Trashes + +# User-local state (never commit) +.local/ +.config/ +*.log + +# Editor / IDE +.idea/ +.vscode/ +.cursor/ + +# User-rendered launchd plist (template lives in launchd/) +*.plist.local diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..736ad41 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 ssr contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9f1aa3c --- /dev/null +++ b/README.md @@ -0,0 +1,220 @@ +# ssr — System Sync & Restore for macOS + +Reproducible macOS setup in one command. `ssr` snapshots your Homebrew bundle and application configs to a cloud drive on a schedule, and can rebuild a fresh Mac from that snapshot. + +- **Backup** (`ssr sync`): dumps `Brewfile` and runs `mackup backup` to the configured cloud directory. +- **Restore** (`ssr restore`): installs Homebrew, replays the `Brewfile`, restores `mackup` configs, applies macOS defaults. +- **Scheduled sync**: native macOS `launchd` agent (daily by default). +- **Cloud storage**: iCloud Drive, Dropbox, Google Drive, OneDrive, or any custom path — selectable via config. +- **No secrets**: all state lives in your cloud drive; the repo only contains code and config templates. + +> Successor to the original `system_sync.sh` / `system_restore.sh`. Same intent, modernized tooling, modular, idempotent, scheduled by default. + +--- + +## Requirements + +- macOS (Apple Silicon or Intel) +- Bash 3.2+ (preinstalled) +- Network access for the initial Homebrew install +- A cloud drive folder you control (iCloud Drive is the default) + +--- + +## Install + +```bash +git clone https://git.surke.de/osurke/system-sync-restore.git ~/.ssr +cd ~/.ssr +./install.sh +``` + +`install.sh` will: + +1. Symlink `bin/ssr` into `~/.local/bin` (override with `SSR_PREFIX`). +2. Seed `~/.config/ssr/ssr.conf` from `config/ssr.conf.example` (chmod 600). +3. Optionally enable the scheduled daily sync. + +If `~/.local/bin` is not on your `PATH`, add it to your shell rc: + +```bash +export PATH="$HOME/.local/bin:$PATH" +``` + +--- + +## Usage + +```text +ssr install First-time install: link binary, seed config, schedule sync +ssr sync One-shot backup → cloud +ssr restore [Brewfile] Restore the machine; Brewfile defaults to the cloud copy +ssr schedule on|off Enable/disable the daily launchd job +ssr config show|edit Show or edit the active config +ssr status Show config, cloud target, last sync, schedule state +ssr version Print version +``` + +### First backup + +```bash +ssr sync +``` + +Output structure in the cloud drive: + +``` +/ +├── BACKUP/ +│ └── - / +│ ├── Brewfile +│ └── Brewfile.bak +└── Mackup/ + └── … (managed by mackup) +``` + +### Restore on a new machine + +```bash +git clone https://git.surke.de/osurke/system-sync-restore.git ~/.ssr +cd ~/.ssr && ./install.sh +ssr restore +``` + +By default `ssr restore` picks the Brewfile from `/BACKUP/ - /Brewfile`. To restore from a different machine's snapshot, pass an explicit path: + +```bash +ssr restore "$HOME/Library/Mobile Documents/com~apple~CloudDocs/BACKUP/oldhost - ABCD-1234/Brewfile" +``` + +--- + +## Configuration + +Config lives in `~/.config/ssr/ssr.conf` (override with `--config` or `$SSR_CONFIG`). The file is sourced by bash but only recognised `KEY=VALUE` lines are accepted; anything else causes `ssr` to abort. + +| Key | Default | Purpose | +|-----|---------|---------| +| `SSR_CLOUD_PROVIDER` | `icloud` | `icloud`\|`dropbox`\|`googledrive`\|`onedrive`\|`custom` | +| `SSR_CLOUD_PATH` | — | Absolute path; only used when provider is `custom` | +| `SSR_BACKUP_SUBDIR` | `BACKUP` | Subfolder for per-host backups | +| `SSR_MACKUP_SUBDIR` | `Mackup` | Subfolder used by mackup | +| `SSR_BREW_UPGRADE` | `true` | Run `brew upgrade` during sync | +| `SSR_BREW_CLEANUP` | `true` | Run `brew cleanup` after sync | +| `SSR_DISABLE_GATEKEEPER` | `false` | Run `spctl --global-disable` during restore (security trade-off) | +| `SSR_OPEN_CASKS` | `false` | Open every installed cask after restore for first-run config | +| `SSR_MACOS_DEFAULTS` | repo's `config/macos-defaults.sh` | Override path to your own defaults script | +| `SSR_LAUNCHD_LABEL` | `de.surke.ssr.sync` | launchd job label | +| `SSR_SCHEDULE_HOUR` | `9` | Daily run hour (0–23, local time) | +| `SSR_SCHEDULE_MINUTE` | `0` | Daily run minute | +| `SSR_PREFIX` | `$HOME/.local/bin` | Install prefix for the `ssr` symlink | +| `SSR_STATE_DIR` | `$HOME/.local/state/ssr` | Internal state directory | +| `SSR_LOG_DIR` | `$HOME/.local/state/ssr/logs` | launchd log directory | + +### Cloud provider paths resolved automatically + +| Provider | Resolved root | +|----------|---------------| +| `icloud` | `~/Library/Mobile Documents/com~apple~CloudDocs` | +| `dropbox` | `~/Library/CloudStorage/Dropbox*` (fallback `~/Dropbox`) | +| `googledrive` | `~/Library/CloudStorage/GoogleDrive-*` | +| `onedrive` | `~/Library/CloudStorage/OneDrive-*` | +| `custom` | Value of `SSR_CLOUD_PATH` | + +--- + +## Scheduled sync + +```bash +ssr schedule on # write & load ~/Library/LaunchAgents/