Personal development environment configuration and setup automation for macOS 26.3+.
This repository contains my dotfiles and an automated setup script to quickly bootstrap a new macOS machine with my preferred development environment, tools, and configurations. It includes shell configurations, editor settings, terminal multiplexer setup, Git configuration, macOS system preferences, and curated development tooling.
git clone https://github.com/jasonfungsing/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
./install.sh
exec zsh- macOS 26.3 or later
- Command line tools for Xcode (installed automatically by script)
- Git (installed automatically if needed)
- Administrator access (required for some macOS settings and Homebrew installation)
- Internet connection (for downloading packages and tools)
- Approximately 5-10 GB of free disk space (for all packages)
| File | Purpose |
|---|---|
.zshrc |
Zsh shell configuration with aliases, plugins, and integrations |
.alias_prompt.sh |
Alias reminder — blocks Enter and suggests the alias when one exists for the typed command |
.config/nvim/* |
Neovim configuration (pure Lua, modular — init.lua plus config/keymaps/plugins/theme/utils) |
.tmux.conf |
Terminal multiplexer (tmux) configuration |
.gitconfig |
Git version control configuration |
.hushlogin |
Suppresses macOS login message |
Organized by category:
- DevOps & Cloud: Docker, Kubernetes (kubectl, helm, minikube, kind, kops, skaffold, stern), Tailscale
- Development: Go, Node.js, Python, Java, Ruby
- Build Tools: Maven, Gradle, Make, CMake, Protobuf
- CLI Utilities: git, tmux, tmuxinator, lazygit, ripgrep, fzf, jq, curl, wget, btop
- Language Tools: golangci-lint, shellcheck
- Databases: RocksDB
- Miscellaneous: Pandoc, Tesseract OCR, Figlet
See the full package list with rationale.
- IDEs: Xcode, Visual Studio Code
- Terminal: iTerm2 (+ Powerline fonts)
- Browsers: Google Chrome
- AI assistants: Claude desktop, Claude Code CLI, Gemini
- Development: Docker Desktop
- Productivity: Raycast, Slack, Setapp, Logi Options+
- Security: Little Snitch, Okta Verify
- Media: AdBlock for Safari, Dark Reader for Safari
Development extensions including Python, Go, Docker, GitHub integration, and Claude Code.
The install.sh script automates the entire setup process:
./install.shWhat it does (in order — apps install before their configs):
- Installs Homebrew, then all Brewfile packages, applications, and VS Code extensions
- Uninstalls brew packages, casks, taps, and VS Code extensions not declared in the Brewfile (the Brewfile is the source of truth — skip with
--no-prune) - Installs Oh-My-Zsh and sets Zsh as the default shell
- Creates symlinks for all dotfiles to your home directory
- Installs tmux plugins, points iTerm2 at its config folder (app/iterm2/), and symlinks VS Code settings (app/vscode/)
- Sets up the Neovim configuration
- Applies macOS system preferences and keyboard shortcuts
- Validates the finished setup
Failure handling: the installer only aborts up front for system-wide problems (not macOS, running as root, no network, incomplete repo clone, Command Line Tools missing and uninstallable — it tries a headless install and the GUI installer first). After that, a failing step never stops the run — the remaining steps continue, and every failure is listed in a summary at the end (with a non-zero exit code). Fix the causes and re-run; all steps are idempotent.
A full installation takes roughly 25-45 minutes, depending on internet and disk speed.
Install only specific components:
./install.sh --shell-only # Only shell config
./install.sh --editor-only # Only editor config
./install.sh --git-only # Only Git config
./install.sh --terminal-only # Only terminal config
./install.sh --system-only # Only macOS settings
./install.sh --no-brew # Skip Homebrew packages
./install.sh --no-apps # Skip applications
./install.sh --no-private # Skip the private dotfiles repo
./install.sh --dry-run # Show what would be doneFlags can be combined, e.g. ./install.sh --no-brew --no-apps.
Licenses and machine-private overrides live in a separate private
GitHub repo, not here. Near the end of a full run, install.sh asks
whether to set it up: answering yes opens a GitHub login in the browser
(if gh isn't already authenticated), clones the private repo to
~/Code/private-dotfiles, and runs its own install.sh, which links
its files into place. Answering no (or --no-private, or a
non-interactive run) skips it — re-run ./install.sh anytime to set it
up later.
If you prefer to install manually:
-
Clone the repository:
git clone https://github.com/jasonfungsing/dotfiles.git ~/.dotfiles cd ~/.dotfiles
-
Create symlinks:
ln -s ~/.dotfiles/terminal/zshrc ~/.zshrc ln -s ~/.dotfiles/terminal/alias_prompt.sh ~/.alias_prompt.sh ln -s ~/.dotfiles/terminal/tmux.conf ~/.tmux.conf ln -s ~/.dotfiles/git/gitconfig ~/.gitconfig ln -s ~/.dotfiles/git/gitignore_global ~/.gitignore_global ln -s ~/.dotfiles/mac/hushlogin ~/.hushlogin # Neovim is modular — link everything, not just init.lua mkdir -p ~/.config/nvim for item in ~/.dotfiles/neovim/*; do [ "$(basename "$item")" = "README.md" ] || ln -s "$item" ~/.config/nvim/ done
-
Install Homebrew and add it to your PATH:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$HOME/.zprofile" eval "$(/opt/homebrew/bin/brew shellenv)"
-
Install Oh-My-Zsh:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" -
Install packages:
brew bundle --file=./brew/Brewfile
-
Set Zsh as default shell:
grep "$(command -v zsh)" /etc/shells || echo "$(command -v zsh)" | sudo tee -a /etc/shells chsh -s "$(command -v zsh)"
-
Apply macOS settings:
sh ~/.dotfiles/mac/macos.sh -
Reload your shell:
exec zsh
On first launch, Neovim bootstraps lazy.nvim and installs all plugins automatically — no manual steps needed. See the Neovim documentation for details.
Edit alias_prompt.sh and zshrc to add or modify aliases. See the terminal documentation for the complete list and explanations.
Three files hold machine-private settings, all loaded automatically when present and all provided by the private companion repo's installer (never tracked here):
~/.zshrc.private— shell extras (keys, work aliases), sourced near the end of zshrc~/.gitconfig.local— git overrides (work identity, proxy), included at the bottom of the shared gitconfig so it wins~/.tmux.private— tmux extras, sourced at the end of tmux.conf
On a machine without the private repo, plain local files at the same paths work identically.
Edit mac/macos.sh to modify system preferences. See the macOS documentation for detailed explanations of each setting.
To add new Homebrew packages:
- Edit
Brewfileand add your package - Run
brew bundle install --file=brew/Brewfileto install
See the Homebrew documentation for the complete package list and rationale.
To update all Homebrew packages:
brew update
brew upgradedotfiles/
├── README.md # This file
├── install.sh # Main installation script
│
├── brew/ # Homebrew configuration
│ └── Brewfile # Homebrew package definitions
│
├── neovim/ # Neovim editor configuration
│ ├── init.lua # Entry point (pure Lua)
│ ├── config/ # Core settings and autocmds
│ ├── keymaps/ # Key mappings
│ ├── plugins/ # Plugin specs
│ ├── theme/ # Colourscheme
│ └── utils/ # Helper utilities
│
├── terminal/ # Terminal configuration (zsh + tmux)
│ ├── zshrc # Zsh shell configuration
│ ├── zshenv # PATH for non-interactive shells (ssh/mosh remote commands)
│ ├── alias_prompt.sh # Custom aliases and prompt
│ ├── cobalt2.zsh-theme # Zsh theme
│ └── tmux.conf # tmux configuration
│
├── git/ # Git configuration
│ ├── gitconfig # Git version control configuration
│ └── gitignore_global # Global ignore rules (core.excludesfile)
│
├── app/ # Application configuration
│ ├── iterm2/
│ │ └── com.googlecode.iterm2.plist # iTerm2 terminal settings
│ ├── vscode/ # VS Code editor configuration
│ │ ├── settings.json # Editor settings (symlinked by install.sh)
│ │ └── keybindings.json # Keybindings (Cmd+Enter → Claude CLI submit)
│ ├── claude/ # Claude Code config (settings, keybindings, status line)
│ ├── copilot/
│ │ └── settings.json # Copilot CLI preferences (symlinked by install.sh)
│ ├── setapp/
│ │ ├── apps.txt # Tracked Setapp app list (no Setapp CLI — installs stay manual)
│ │ ├── export-apps.sh # Regenerate apps.txt from /Applications/Setapp
│ │ ├── export-prefs.sh # Export each Setapp app's settings to prefs/
│ │ └── prefs/ # App settings plists (imported on fresh machines only)
│ ├── sublime-text/
│ │ └── User/ # Whole Packages/User dir (symlinked by install.sh)
│ └── sublime-merge/
│ └── User/ # Whole Packages/User dir (symlinked by install.sh)
│
└── mac/ # macOS system configuration
├── macos.sh # macOS system preferences script
├── export-shortcuts.sh # Export keyboard shortcuts to JSON
├── keyboard-shortcuts.json # Saved keyboard shortcuts
└── hushlogin # Suppress macOS login message
Some failures need a human — they'll appear in the end-of-run failure summary, and these are the fixes:
| Failure | Manual fix |
|---|---|
| App Store apps fail ("Not signed in") | Sign into the App Store app, re-run |
| App Store app not purchased on this Apple ID | Buy/accept it in the App Store once, re-run |
| Cask adopt fails on an app you installed by hand (version mismatch) | Trash the old app copy, re-run (brew installs a fresh one) |
| "Operation not permitted" modifying an app bundle, even with sudo | System Settings → Privacy & Security → App Management → enable your terminal, restart it, re-run |
| Little Snitch / network filter blocks downloads | Approve the connections in the filter, re-run |
| Corporate proxy breaks TLS downloads | Set HTTPS_PROXY/trust the proxy cert per IT instructions |
| MDM-managed apps (CrowdStrike Falcon, Workspace ONE) | Deliberately not brew-managed — leave them to IT |
| System-extension apps (Little Snitch) installed but inert | Approve the extension in System Settings when prompted |
If the script exits with an error:
- Ensure you're in the dotfiles directory:
cd ~/.dotfiles - Make the script executable:
chmod +x ./install.sh - Run with bash explicitly:
bash ./install.sh - Check your internet connection and review the error message
Do not run the installer with sudo — it refuses to run as root (Homebrew won't, and symlinks would land in root's home). Run it as your normal user; the steps that genuinely need admin rights prompt for your password themselves.
No action needed — the installer is safe to re-run and always converges on
this repo's state. Correct symlinks are left alone, wrong or dangling ones
are re-pointed, and a pre-existing real file is backed up to
<name>.backup.<timestamp> before being replaced, so nothing is lost.
Or remove it outright:
rm ~/.zshrc && ./install.shVerify installation:
echo $SHELLIf not zsh, ensure it is listed in /etc/shells and set it as default:
grep "$(command -v zsh)" /etc/shells || echo "$(command -v zsh)" | sudo tee -a /etc/shells
chsh -s $(which zsh)Ensure you have Command Line Tools installed:
xcode-select --installAlso check free disk space (df -h) and run brew doctor.
Some packages may require additional dependencies. Check individual package documentation or run:
brew doctorIf brew bundle fails on a specific package, verify it exists with brew search <package-name> and try installing it individually with brew install <package-name>.
Re-run the settings script:
./mac/macos.shSome settings require a logout/login. To apply immediately:
killall Finder Dock Mail SystemUIServerA full ./install.sh run validates the setup automatically at the end. To
validate on its own — after a partial install, or any time something feels
off:
./install.sh --validateThis checks:
- Every symlink exists, points at the right file in this repo, and resolves (dangling symlinks fail)
- Configs actually load: zsh syntax + interactive startup, Neovim headless launch, tmux config parse, git user.name
- Zsh is the default shell and Oh-My-Zsh is present
brew bundle checkconfirms the machine matches the Brewfile- Key tools (git, jq, node, python3, go) are on the PATH
- iTerm2 loads its preferences from app/iterm2/
- Repo health: keyboard-shortcuts.json is valid JSON, install.sh parses
It exits 0 only when every check passes.
You can also spot-check individual components manually:
echo $SHELL # Should show /opt/homebrew/bin/zsh or /usr/bin/zsh
alias | grep "^t=" # Should show tmux alias
git config user.name # Should show your name
brew --version # Should show Homebrew versionTo keep your environment up to date:
u # Update all packages, casks, Oh-My-Zsh, and Neovim pluginsOr use the full command:
update # brew update + upgrade (incl. casks) + cleanup, omz update, then
# nvim --headless "+Lazy! sync" +qa to update Neovim pluginsTo remove the dotfiles configuration:
# Remove symlinks
rm ~/.zshrc
rm ~/.tmux.conf
rm ~/.gitconfig
rm ~/.alias_prompt.sh
rm ~/.hushlogin
rm ~/.oh-my-zsh/custom/themes/cobalt2.zsh-theme
find ~/.config/nvim -maxdepth 1 -type l -delete # all Neovim config symlinks
# Set bash back as default shell
chsh -s /bin/bash
# Remove dotfiles directory (if desired)
rm -rf ~/.dotfilesFor detailed information, see:
This dotfiles configuration supports a modern development stack focused on:
- Cloud & DevOps: Docker, Kubernetes, Tailscale
- Backend Development: Go, Python, Node.js, Java, Ruby
- Development Tools: Git, tmux, Neovim, VS Code
- Infrastructure: Helm, Kops, Minikube, Skaffold
These are personal dotfiles, but feel free to fork and adapt to your needs.
Personal use only. Feel free to use as reference for your own dotfiles.
For issues or questions:
- Check Troubleshooting section
- Review the relevant folder's README (brew, mac, terminal, neovim)
- Check macOS and Homebrew documentation (
brew help,man zsh,man tmux)
Last Updated: July 14, 2026 macOS Version: 26.3+ Shell: Zsh Package Manager: Homebrew