Day 1, Fresh MacBook, 2 Hours Lost - So I Built The MacSetup
Day 1
Day one at the new gig. I'm a DevOps engineer, which means I spend most of my professional life automating away other people's toil. So it was genuinely humbling when IT handed me a fresh M2 MacBook, still smelling like the inside of an Apple shipping carton — and the first task of my new role turned out to be: "provision my own laptop, by hand, like it was 2023".
I clicked through the macOS welcome flow. Signed into the work account. Stared at an default items in Dock. And then started doing the same thing I'd done on every previous machine — except from memory, because my old laptop was already back in another company's asset-tracking spreadsheet.
First: a macOS upgrade, because of course the factory image was a minor version behind. Then Homebrew. Then opening tabs while I tried to remember the exact jq / yq / pnpm / nvm lineup I always end up at. Then googling the cask name for Docker container manager for Mac because I could never remember its name. Then a .zshrc block that was technically correct but spiritually wrong. Then nvm installed, node not on $PATH, and a small, growing suspicion that I had become the very SRE meme I usually post in Slack.
At some point I ran git config --global user.email for the new work address, and immediately thought "okay, but don't forget to not use this when you push to your personal repos tonight." That is, of course, exactly how you accidentally commit to a public OSS repo with truongtbn@employer.com. We do not speak of this.
Total damage: roughly two hours of upgrades, hunting for tools, installing them, and tweaking config until things felt like home. Two hours of an automation engineer doing the least automated thing imaginable.
That evening I opened my personal laptop and wrote the script I should have written two laptops ago.
That script is macsetup.
A disclaimer up front: this is my setup. The app list, the fonts, the CLI tools — they reflect my taste and my workflow as a DevOps engineer. It's not a universal "best Mac setup". It's the one I wanted on day one. Fork it, change it, make it yours.
What it does
macsetup is a single shell script that provisions a fresh macOS dev machine in one shot or in pieces. The feature list is small on purpose.
One-line quick start.
curl -fsSLO https://raw.githubusercontent.com/nh4ttruong/macsetup/main/setup && zsh setup all
Pulls the script, runs every group with no prompts. Go make coffee. Come back. Done.
Modular groups. Pick what you need:
| Group | What you get |
|---|---|
brew |
Homebrew, installed or updated; prereq for everything else |
zsh |
zsh-completions + a sentinel-fenced config block in ~/.zshrc |
node |
nvm + Node.js (prompts for version, default 24) |
cli |
git, make, jq, yq, nano, nanorc, pnpm, telnet |
apps |
VS Code, iTerm2, Edge, Telegram, XKey, Termius, OrbStack |
fonts |
Fira Code, Hack, Source Code Pro, DejaVu — Nerd Font variants included |
git |
N renamable identity profiles + a gitswitch helper |
./setup brew cli git is a valid command. So is ./setup apps. The script does not have feelings about what you skip.
Per-group item picker. For multi-item groups (cli, apps, fonts), you can pick specific items by number — 1 3 5 installs only those — instead of taking the whole bundle.
Idempotent. Safe to re-run. Existing installs stay. Config blocks get matched by sentinels, not duplicated. Run it once, run it eleven times, same result. (I have tested this. Empirically. Many times.)
gitswitch for multiple identities. The feature I personally needed the most.
I like contributing to open source. I write OSS in my own time. Which means my personal repos need to be signed with my personal identity — not firstname.lastname@employer.com, and definitely not whatever name HR put on my work account. Switching identities by hand with git config user.email works until the day it doesn't, and that day always shows up as a public commit.
So the git group sets up N profiles (default personal and work, rename them to anything), writes them to ~/.gitswitch.conf with chmod 600, and drops a gitswitch helper on your $PATH:
gitswitch nh4ttruong # set local user.name + user.email for this repo (OSS time)
gitswitch truongtbn # switch back for the day job with domain name
gitswitch list # show configured profiles
One command per repo. No more guessing which email I'm about to commit as.
How to try it
git clone https://github.com/nh4ttruong/macsetup.git
cd macsetup
./setup
Read the script before you run it. It's bash. It's short. You can audit the whole thing in a coffee break — which, as a DevOps engineer, is exactly the bar I want for anything that touches my $HOME.
Contributing
This is the part I actually care about.
macsetup is my setup. That's its strength (it's opinionated and small) and its limit (your tools aren't my tools). I'd love for it to grow — not into a framework, but into a collection of sane defaults that other engineers have battle-tested.
Ways to help, in increasing order of commitment:
Open an issue if something breaks on your machine. Apple Silicon, Intel, fresh install, restored-from-Time-Machine — all interesting.
Suggest a tool or app you think belongs in a group. Bonus points if you explain why (the why is the whole readme).
Send a PR that adds a new group, a new item, or improves idempotency on something I missed. The bar is "would a tired DevOps engineer be glad this was here on a Monday morning."
Fork it and never speak to me again. That's also a legitimate contribution to the universe.
The CONTRIBUTING has the details. The repo is here: https://github.com/nh4ttruong/macsetup
There's going to be a next Mac. For me, for you, for the new hire who joins your team in the future. Let's make day one shorter.





