From Fresh Mac to Productive in 30 Minutes
I’ve been hearing developers talk about their dotfiles for years. “Oh yeah, I just clone my dotfiles repo and I’m set up in minutes.” I had some semblance of a setup script at one point, but I kinda just went with manual mode for fresh Mac setups.
Even if I had most apps installed with a script, weeks later when I needed a tool, I realized I forgot to install it. Also, macOS settings that I had are missing and I forget where to find them. One big hot mess of a fresh install.
I recently decided to wipe my Mac for a clean slate. I didn’t want to spend days piecing my setup back together app by app, so I finally committed to creating a proper dotfiles repo. With a dash of AI, I was on my way to making a great install setup.
The https://github.com/nickytonline/dotfiles repository on GitHubDotfiles to the Rescue#
The concept is simple. All those configuration files that start with a dot (.zshrc, .gitconfig, etc.) can be version controlled and shared across machines.
A dotfiles repository is not macOS specific, but I wanted to go further, my whole Mac setup or most of it.
Here’s what I ended up with:
~/dotfiles/├── Brewfile # Every app I use├── install.sh # One script to rule them all├── macos-defaults.sh # macOS system preferences├── shell/ # Shell configs (.zshrc, etc.)├── git/ # Git configuration├── ssh/ # SSH config (no keys!)└── config/ # App configs (starship, gh, atuin, etc.)Brewfile’s Got Your Apps’ Backs#
If you’re on macOS and not using Homebrew, you’re missing out. It’s basically apt or yum for macOS.
What I didn’t fully appreciate until this project was Homebrew’s Brewfile feature. I knew it existed but never bothered to create one. If you already have Homebrew installed, you can generate a Brewfile from everything you currently have installed:
brew bundle dumpThis creates a Brewfile in your current directory with all your brew packages, casks, Mac App Store apps.
Instead of running brew install commands one by one, you declare everything in a single file:
# CLI toolsbrew "git"brew "ripgrep"brew "fnm" # Node version manager - https://github.com/Schniz/fnmbrew "starship" # Beautiful shell prompt - https://starship.rs# ...
# Desktop apps (casks)cask "visual-studio-code@insiders"cask "raycast"cask "1password"# ...
# Rust cratescargo "oha"# ...Once you have your Brewfile, you can use it to install all your apps:
brew bundle --file=BrewfileHere’s my Brewfile if you wanna check it out.
Look mas!#
I always thought Mac App Store apps couldn’t be automated. Turns out, there’s a tool called mas (Mac App Store CLI) that allows you to automate installation of Mac App Store apps.
# In your Brewfilebrew "mas"
# Then add Mac App Store appsmas "Dato", id: 1470584107mas "Keynote", id: 409183694mas "Numbers", id: 409203825mas "TestFlight", id: 899247664# ...When you run brew bundle, it installs mas first, then uses it to download and install Mac App Store apps automatically.
To find app IDs, just search on the Mac App Store website and grab the ID from the URL, e.g. 1470584107 is the app ID for Dato, https://apps.apple.com/us/app/dato/1470584107.
Or just run mas list to see all the apps you currently have installed and snatch the app IDs from there.
My Install Script#
Here’s my install.sh that I orchestrates the whole setup. The script is idempotent and prompts for each major step.
#!/usr/bin/env bash
# Install Xcode Command Line Tools firstif ! xcode-select -p &> /dev/null; then xcode-select --install read -n 1 -s -r # Wait for completionfi
# Install Homebrewif ! command -v brew &> /dev/null; then /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"fi
# Symlink all dotfilescreate_symlink "$DOTFILES_DIR/shell/.zshrc" "$HOME/.zshrc"create_symlink "$DOTFILES_DIR/git/.gitconfig" "$HOME/.gitconfig"# ... and more
# Install Rust (before brew bundle, so cargo packages work)curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# Install everything from Brewfilebrew bundle --file="$DOTFILES_DIR/Brewfile"
# Install Node.js LTS via fnmfnm install --ltsnpm install -g @openai/codex
# Apply macOS system preferences./macos-defaults.shAutomating macOS Preferences#
Like I mentioned in the beginnging, this is more than a dotfiles repository. It also covers macOS settings like Dock position and size, Finder settings, keyboard repeat rate, menu bar clock settings etc.
All of these can be automated with defaults write commands:
# Position Dock on the rightdefaults write com.apple.dock orientation -string "right"
# Show hidden files in Finderdefaults write com.apple.finder AppleShowAllFiles -bool true
# Fast keyboard repeatdefaults write NSGlobalDomain KeyRepeat -int 2
# Show analog clock with day of weekdefaults write com.apple.menuextra.clock IsAnalog -bool truedefaults write com.apple.menuextra.clock ShowDayOfWeek -bool true
...Here’s my macOS settings with both recommended defaults for devs and my personal preferences, commented so I can pick and choose.
What Not to Commit to Your Dotfiles Repo#
A lot of devs showcase their dotfiles as a public repository on GitHub, so do not add:
- API tokens or credentials
- SSH private keys
.npmrcfiles with auth tokens- Work-related hostnames or company names
- Personal email addresses (if you care about that)
- etc.
To prevent this, use your .gitignore file to prevent certain files from being committed to the repository:
.npmrc**/*_token**/*_key*.local.ssh/config_local.env.env.**.env.env.*.localGit Setup#
My install script creates a symlink for my Git config, and from there I just need to set up my signing key and access key If not already configured.
Modern Git Signing with SSH#
While setting this up, I learned you can sign git commits with SSH keys instead of GPG, which is what I was previously doing. So the SSH key you use for getting git access via SSH can also be used for signing your commits. No more dealing with GPG key expiration or complex setup:
# Tell git to use SSH for signinggit config --global gpg.format sshgit config --global user.signingkey ~/.ssh/id_ed25519.pubgit config --global commit.gpgsign true
# Add your SSH key as a "Signing Key" on GitHub# (not just an authentication key!)GitHub shows commits as “Verified” just like with GPG, but it’s way simpler.
Productive in 30 Minutes#
Now when I get a new Mac or when I need to refresh my existing one (that’s what prompted me to automate this), the process is:
-
Clone the repo:
Terminal window git clone https://github.com/nickytonline/dotfiles.git ~/dotfilescd ~/dotfiles -
Run the install script:
Terminal window ./install.sh -
Say “yes” to the prompts and watch the magic happen:
- Xcode Command Line Tools install
- Homebrew installs
- My apps download and install automatically
- All dotfiles symlink
- Rust, Node.js, and CLI tools set up
- macOS preferences apply
-
5 minutes of manual work:
- Generate/restore SSH keys
- Sign into 1Password
- Run
gh auth login - Sign into a few apps (Slack, Raycast, etc.)
Boom! All set up.
Try It Yourself#
Setting up dotfiles is easier than you think. Start small:
- Create a
~/dotfilesdirectory - Move your
.zshrcthere and symlink it:ln -s ~/dotfiles/.zshrc ~/.zshrc - Initialize a git repo:
git init - Create a Brewfile:
brew bundle dump - Build from there
Your future self (and your next Mac) will thank you.
Check out my dotfiles repo if you want to see the full setup or steal parts of it. If you’ve got your own dotfiles setup or discovered other tools that make Mac automation easier, I’d love to hear about it.
If you want to stay in touch, all my socials are on nickyt.online.
Photo by Ales Nesetril on Unsplash