This is the universal installer script used to install 'set-me-up' (smu) on a Mac, debian, arch based machine.
To start, your default shell must be set to bash prior to executing the install snippet for the first time. This is because on newer versions of Mac OS, the default shell is zsh instead of bash. To change your default shell, run the following command in your console.
sudo chsh -s $(which bash) $(whoami)Once the default shell is bash, close and reopen the terminal window. Then, run the following command in your console.
(install snippet if you don't fully
understand what it does. Seriously, DON'T!)
bash <(curl -s -L https://raw.githubusercontent.com/dotbrains/set-me-up-installer/main/install.sh)You can change the smu home directory by setting an environment variable called SMU_HOME_DIR. Please keep the variable declared or else the smu scripts are unable to pickup the sources.
export SMU_HOME_DIR="some-path" \
bash <(curl -s -L https://raw.githubusercontent.com/dotbrains/set-me-up-installer/main/install.sh)smu resolves a module name like productivity-tools/hyperkey against the directory tree at $SMU_HOME_DIR/dotfiles/modules/, which is laid out by OS bucket:
$SMU_HOME_DIR/dotfiles/modules/
├── macos/ # MacOS-only modules
├── debian/ # Debian/Ubuntu-only modules
├── arch/ # Arch-only modules
└── universal/ # Modules that work on any supported OS
A module is any directory under one of those buckets that contains a matching <name>.sh script, a brewfile, or (on Debian-based systems) a packages file. The path you pass to -m is the path relative to the bucket — for example, modules/macos/productivity-tools/hyperkey/hyperkey.sh is invoked as:
smu -p --no-base -m productivity-tools/hyperkeyThe community-maintained module collections live in their own repositories — browse these to see what's available and to crib examples when authoring your own:
- dotbrains/set-me-up-macos-modules
- dotbrains/set-me-up-debian-modules
- dotbrains/set-me-up-universal-modules
To see the modules currently available in your $SMU_HOME_DIR, use -l / --list-modules:
smu -lOutput is grouped by OS bucket. Each entry is tagged [script], [brewfile], or [packages] so you know what kind of module it is, and the name shown is the exact value you'd pass to -m:
macos/
productivity/hyperkey [brewfile]
terminal/alacritty [script]
debian/
browsers/chrome [packages]
development-tools/cursor [script]
universal/
python/pip [script]
shell [brewfile]
Found 6 module(s) (showing 'macos' + 'universal'; use --all to include other OS buckets).
Run a module with: smu -p --no-base -m <module>
By default the list hides modules that don't apply to the current OS. Pass --all to include every bucket:
smu -l --allTo narrow the list, pass --search <query> (case-insensitive substring match against the module name):
smu -l --search hyper
smu -l --search python --allFor a faster workflow, use -i / --interactive to launch an fzf-powered multi-select picker. Type to fuzzy-filter, press SPACE (or TAB) to toggle a module, and press ENTER to provision everything you selected:
smu -i --no-base-i honors the same filters as -l:
smu -i --search node # pre-fill the fzf query with "node"
smu -i --all # include modules from other OS bucketsSelected modules are run through the same provisioning pipeline as -p -m ..., including the -b / --no-base flags. Requires fzf to be installed (brew install fzf, apt install fzf, or pacman -S fzf).
Use -st / --status to see which modules are currently installed on the machine. Detection is read-only and never prompts:
smu --status
smu --status --search font
smu --status --all # include modules from other OS buckets
smu --status -V # verbose: show per-entry detail (e.g. "2/3 entries present")Output lists every visible module with a state tag and a count summary at the bottom:
debian/
browsers/chrome [packages] [OK] installed
development-tools/cursor [script] [OK] installed
development-tools/zed [script] [--] missing
fonts/fira-code [script] [??] unknown (no fira-code.installed marker)
3 module(s) (showing 'debian' + 'universal'; use --all to include other OS buckets):
1 installed, 1 missing, 0 partial, 1 unknown
State meanings:
| Tag | Meaning |
|---|---|
[OK] installed |
The module's payload is fully present. |
[--] missing |
The module's payload is fully absent. |
[~~] partial |
Some entries from a packages file are present, others aren't. |
[??] unknown |
A *.sh module without a sibling <name>.installed marker (smu refuses to guess). |
How each kind is detected:
brewfile→brew bundle check --file <brewfile> --no-upgrade.packages→ each entry checked individually (dpkg -s,snap list,sources.list.dlookups). Reportspartialwhen only some entries are present.*.sh→ if the module ships an opt-in<name>.installedsibling, smu sources it underutilities.sh; exit 0 means installed. Without the marker the module reportsunknown.
Use -u / --uninstall to undo a module's install. Brewfiles and packages files are reversed declaratively; *.sh modules require an opt-in <name>.uninstall.sh sibling, otherwise they're surfaced as "cannot auto-uninstall — manual cleanup required" and skipped.
smu -u -m media/spotify productivity/raycast # prompts [y/N]
smu -u -m media/spotify --dry-run # show the plan, change nothing
smu -u -m media/spotify -y # skip the prompt (scripts/CI)
smu -iu # fzf picker, multi-select uninstallThe plan is shown before any destructive action so you can sanity-check it:
The following will be uninstalled:
- development-tools/cursor (cursor.uninstall.sh ; apt_remove_from_file packages)
- browsers/chrome (apt_remove_from_file packages)
Cannot auto-uninstall:
! installers (no installers.uninstall.sh)
Continue? [y/N]
How each kind is reversed:
brewfile→brew bundle cleanup --file <brewfile> --force.packages→apt_remove_from_file packages(mirrorsapt_install_from_file's regex set:apt remove,snap remove,add-apt-repository --remove,rmfor source lists / gpg keyrings).*.sh→ sources sibling<name>.uninstall.sh. Modules that share their directory with apackages(Debian) orbrewfile(macOS) file run both inverses in order: per-module uninstaller first (apt repos, signing keys, vendor dirs the install script added), then the declarative cleanup (shared dependencies the install script asked for).
Two optional files alongside <name>.sh opt the module into the status and uninstall flows:
-
<name>.installed— sourced by--status. Exit 0 means installed; non-zero means missing. Keep it terse:# development-tools/cursor/cursor.installed package_is_installed "cursor"
-
<name>.uninstall.sh— sourced by--uninstall. Same shape as the install script: sourceutilities.sh, guard withis_macos/is_debian, do the inverse work. Don't re-undo what a siblingpackages/brewfiledeclares — smu chains those automatically:# development-tools/cursor/cursor.uninstall.sh source "$HOME/set-me-up/dotfiles/utilities/utilities.sh" main() { if ! is_debian; then error "Debian only!"; return 1; fi ask_for_sudo sudo apt-get remove --purge -y cursor &> /dev/null sudo rm -f /etc/apt/sources.list.d/cursor.list sudo rm -f /etc/apt/keyrings/cursor.gpg sudo apt-get autoremove -qqy &> /dev/null } main
Without these sibling files a module installs as before but reports unknown under --status and is skipped by --uninstall — never silently broken.
The creator of this repo is not responsible if your machine ends up in a state you are not happy with.
Yes please! This is a GitHub repo. I encourage anyone to contribute. 😃
This project is licensed under the PolyForm Shield License 1.0.0 -- see LICENSE for details.
