Surface unmanaged .app bundles during sync as Markdown report
`ssr sync` now scans /Applications (and ~/Applications, configurable via
SSR_APP_DIRS) for top-level .app bundles that are neither Mac App Store
installs (Contents/_MASReceipt/receipt) nor Homebrew Cask installs, and
writes <backup_dir>/unmanaged-apps.md alongside the Brewfile. This closes
a long-standing gap in the restore story: such apps need manual reinstall
on a fresh machine, and were previously invisible to the sync workflow.
Cask detection runs fully offline against the local Caskroom and unions
four complementary strategies:
1. Caskroom walk for .app symlinks/bundles under
$(brew --caskroom)/<cask>/<version>/.
2. `app "Foo.app"` declarations parsed from .rb formula receipts.
3. `name "Foo"` field from .rb receipts (handles pkg-based casks like
Microsoft Edge, Teams, Zoom, Google Drive, etc., that never drop
a .app under the caskroom).
4. `"name":["Foo"]` field from modern .json receipts (multi-line aware).
For each unmanaged app the report includes version and bundle id from the
Info.plist plus a best-effort installer link, in preference order:
1. Sparkle SUFeedURL → derive the vendor homepage from the URL host.
2. Bundle-id prefix match against a curated vendor map (Adobe,
Microsoft, Docker, Cursor, Anthropic, OpenAI, JetBrains,
GitKraken, Signal, Zoom, Telegram, Slack, Synology, Jabra, ...).
3. DuckDuckGo search fallback (`<App name> macOS download`).
Toggles in ssr.conf.example: SSR_SCAN_UNMANAGED_APPS=true (default),
SSR_APP_DIRS=/Applications:~/Applications.
ssr-status gains a one-line summary referencing the report file.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -70,11 +70,25 @@ Output structure in the cloud drive:
|
||||
├── BACKUP/
|
||||
│ └── <hostname> - <hardware-uuid>/
|
||||
│ ├── Brewfile
|
||||
│ └── Brewfile.bak
|
||||
│ ├── Brewfile.bak
|
||||
│ └── unmanaged-apps.md # apps installed manually (not brew/MAS)
|
||||
└── Mackup/
|
||||
└── … (managed by mackup)
|
||||
```
|
||||
|
||||
During each sync the unmanaged-apps scan compares every `.app` bundle in `/Applications` (and `~/Applications`) against:
|
||||
|
||||
1. **Mac App Store** — bundle contains `Contents/_MASReceipt/receipt`
|
||||
2. **Homebrew Cask** — bundle filename matches the `.app` artifact of any installed cask
|
||||
|
||||
Anything not matched is written to `unmanaged-apps.md` (Markdown table, one row per app, with version, bundle id and a best-effort installer link) and printed to the sync log as a warning. Disable with `SSR_SCAN_UNMANAGED_APPS=false`.
|
||||
|
||||
Link selection for each app uses, in order:
|
||||
|
||||
1. **Sparkle feed** — `SUFeedURL` from the bundle's `Info.plist` → linked vendor homepage.
|
||||
2. **Known vendor map** — bundle-id prefix maps to the official download page (Adobe, Microsoft, Docker, Cursor, Anthropic, JetBrains, GitKraken, Signal, Zoom, etc.).
|
||||
3. **Web search** — DuckDuckGo query for `<App name> macOS download`.
|
||||
|
||||
### Restore on a new machine
|
||||
|
||||
```bash
|
||||
@@ -103,6 +117,8 @@ Config lives in `~/.config/ssr/ssr.conf` (override with `--config` or `$SSR_CONF
|
||||
| `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_SCAN_UNMANAGED_APPS` | `true` | List `.app` bundles not from brew/MAS during sync |
|
||||
| `SSR_APP_DIRS` | `/Applications:~/Applications` | Colon-separated dirs scanned for unmanaged apps |
|
||||
| `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 |
|
||||
@@ -217,6 +233,7 @@ ssr update --dry-run # show what would change, don't pull
|
||||
│ ├── cloud.sh # Provider abstraction
|
||||
│ ├── brew.sh # Homebrew install + Brewfile ops
|
||||
│ ├── mackup.sh # Mackup install + backup/restore
|
||||
│ ├── apps.sh # Detect unmanaged .app bundles (not brew/MAS)
|
||||
│ └── macos.sh # macOS-specific actions (Rosetta, defaults, etc.)
|
||||
├── config/
|
||||
│ ├── ssr.conf.example # Reference user config
|
||||
|
||||
Reference in New Issue
Block a user