// docs

Using migration-machine

Run capture on the old Mac, carry the data folder over, then provision on the new one. Always dry-run first.

Guided setup

The fastest path: one command, then a clickable setup. No git clone, no flags.

curl -fsSL https://migration-machine.callmesukhi.com/install.sh | bash

It downloads migration-machine and launches a guided UI (powered by swiftDialog) that walks you through capturing the old Mac or setting up the new one, builds a manifest for you, and previews every step before anything runs. Already have the repo? Run ./migrate wizard.

Read before you pipe to a shell. Save install.sh, skim it, then run it. Either way, the setup/provisioning path previews everything as a dry run before it changes a thing; the old-Mac backup/capture path runs after you confirm.

Install

It's plain bash with no runtime to install. Clone it on each machine.

git clone https://github.com/callmesukhi/migration-machine.git
cd migration-machine

Or install it with Homebrew:

brew install callmesukhi/tap/migration-machine
migrate wizard

Tool vs data

The repo is the tool. Your captured config is data, kept in a separate folder so you can carry it between machines. Point at it with --data DIR or the MIGRATION_DATA environment variable. Default: ~/migration-data.

Carry one folder. A cloud-synced directory, a USB drive, anything you trust. The secrets inside it are encrypted, but the passphrase is the only wall, so choose a strong one.

Commands

CommandWhat it does
migrate captureExport this Mac's config into the data folder (run on the OLD Mac).
migrate provision -m MProvision a fresh Mac from manifest M (run on the NEW Mac).
migrate provision --listList available manifests.
migrate provision -m M --dry-runPreview every step. Changes nothing.
migrate provision -m M --only IDRun a single step by id.
migrate restore --only PHASERestore captured config only (no provisioning).
migrate bootstrap -m MEnsure Xcode CLT, then provision (bare Mac).

A global --data DIR goes before the command. MIGRATE_UI=cli|gui|auto forces the progress UI.

On the old Mac

Grant your terminal Full Disk Access first (System Settings → Privacy & Security), or some files silently skip.

./migrate --data ~/Sync/migration capture

Set a strong passphrase for the encrypted secrets bundle when prompted, then let your sync finish.

On the new Mac

Copy an example manifest, point it at your dotfiles repo, dry-run, then go.

cp manifests/example-homebrew.json manifests/local-mymac.json
$EDITOR manifests/local-mymac.json

./migrate --data ~/Sync/migration provision -m local-mymac --dry-run
./migrate --data ~/Sync/migration bootstrap  -m local-mymac
Run from a Terminal, not a pure GUI launch. Secrets restore prompts for the passphrase and several steps use sudo; those prompts need a terminal.

Manual checklist

No script can do these. Budget an hour.

  • Re-grant app permissions: Full Disk Access, Accessibility, Screen Recording, Input Monitoring. These live in macOS's TCC database and are deliberately not transferable.
  • Sign back into apps (Slack, 1Password, browsers, Spotify). Preferences restore; sessions do not.
  • Re-enter license keys for paid, non-subscription apps.
  • Keychain: turn on iCloud Keychain on both Macs, or export items manually. Most "why is this broken" traces back to a missing Keychain entry.
  • Browser profiles: sign into your browser account to restore extensions and history.
  • Your data (Documents, Photos, Mail, repos) moves separately. This tool is config only.
  • Verify keys: ssh-add -l, a test git fetch, and gpg --list-secret-keys.

Troubleshooting

A file is missing from capture. Check the capture log for a WARN. Usual cause is Full Disk Access not granted.

An app ignored its restored prefs. It was running during import and overwrote them on quit. Quit it, then --only restore-config.

The MacPorts step stops. MacPorts has no one-line installer; set config.macportsPkgUrl to the .pkg for your macOS version.

VS Code extensions did not install. Enable the code CLI in VS Code, then re-run the step.

More detail lives in docs/RUNBOOK.md in the repo.