Welcome to my personal dotfiles!
This repository contains instructions, configuration files, and bootstrapping scripts for setting up a new macOS machine from scratch. It helps me keep a reproducible workflow for setting up and maintaining my macOS development environment across multiple machines. It also serves as a backup for my configuration files, and a way to share my setup with others.
Feel free to steal, modify, and/or adapt! 🚀
The following steps assume that you are starting from a fresh macOS installation. If that’s not the case, feel free to skip some of the steps at your own discretion.
Before you start, make sure you have a good internet connection and that you are not running on battery power. Depending on your machine's hardware and internet connection, some of the steps below could take a while to complete, and you don't want to run out of battery in the middle of the process.
Important
The following prerequisites are essential for successfully setting up your macOS machine. Without them, you will almost certainly run into issues during the bootstrapping process.
- Make sure you have admin access to your macOS machine.
- Make sure you are logged in with a valid Apple ID (check both System Preferences and the App Store).
- Make sure you've opened and quit both Safari and the Mail app at least once. This is necessary to make sure their respective system preferences files have been created.
- Some of the steps in the bootstrapping script below might require full disk access from your terminal emulator. This is something that you probably want to enable anyway. For instance, iTerm2 will ask you to give it full disk access at first launch. Do to this, navigate to
System Preferences -> Privacy & Security -> Full Disk Access
and toggle the checkbox for your terminal emulators.
To clone this repository under ~/.dotfiles
, run the following command:
git clone https://github.com/tpvasconcelos/dotfiles.git ~/.dotfiles
This directory will contain all the necessary scripts and configuration files to set up your macOS machine. Again, feel free to fork, modify, and adapt any of the files to suit your own needs and preferences.
Note
At this point, you should be prompted to install macOS's Command Line Developer Tools. Please follow the steps in the user interface. If for some reason you are not prompted at this point, don't worry, as we will verify these requirements later on during the bootstrapping process.
I use Homebrew (brew
) as my go-to macOS package manager. Rare are the cases where Homebrew is not enough! Run the following command to install Homebrew on your machine.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
To make brew
available on your shell, you'll need to run the following command first. Note that you won't have to do this again, after you finish going through the bootstrap instructions.
eval "$(/opt/homebrew/bin/brew shellenv)"
Some files in this repository are encrypted using git-crypt
. This allows me to pick and choose which dotfiles I want to publicly share and which ones I would like to keep private. You can see the full list in the .gitattributes
file. You might want to do the same thing when maintaining your own fork.
Starting from a fresh macOS installation, you will need to install the following dependencies using Homebrew:
brew install gnupg git git-crypt
Then, you will need to import your private GnuPG key (I keep mine safely stored in my password manager). You can do this by saving your key in a local temporary text file (e.g. /tmp/gpg-private.key
), and then run:
gpg --import --allow-secret-key-import /tmp/gpg-private.key
Finally, you can unlock the repository using the imported GnuPG key with
git-crypt unlock
Note: here's how you can print your current GnuPG keys to stdout
gpg --export --armor $GPG_KEY_ID
gpg --export-secret-keys --armor $GPG_KEY_ID
The following shell script will run all necessary installation and configuration steps. Feel free to peak inside to see what’s going on. It is recommended to reboot your machine after running this script for the first time. For convenience, the script will prompt you for an automatic reboot at the end.
cd ~/.dotfiles && ./bootstrap.zsh
Note
By default, this script will install a bunch of Python versions (using pyenv
). If you want to control which versions are installed, you can expose a PYENV_VERSIONS
environment variable with a space-separated list of Python versions. For example, to only install Python 3.8 and Python 3.9, run: PYENV_VERSIONS="3.8.9 3.9.3" ./bootstrap.zsh
. Check inside the bootstrap.zsh
script to see which versions are installed by default.
Tip
There are no unwanted side effects from running this script multiple times, and the steps that have already run successefully will simply be skipped. So, if you encounter any issues, feel free to just run the ./bootstrap.zsh
script again (once you've fixed the underlying error on your end).
The following is a collection of useful housekeeping commands that you can run to keep your system up to date and in good shape. The source code for these commands can be found in shell/functions/hc.zsh.
Run hc-doctor
to check for potential issues with your system's state or configuration:
hc-doctor
This script will run a series of checks, such as:
- Check for outdated software.
- Check for issues with this
.dotfiles/
repository. - Verify the status of your GnuPG keys.
- Check for issues with your installed brew packages (brew doctor) or brew bundle (installed packages not listed in the global Brewfile).
If you want to update (almost) everything in your system with a single command, you can run
hc-update-everything --system --brew-greedy-latest --flutter
For simple routine updates, you can just call the hc-update-everything
helper without any arguments.
hc-update-everything
You can also call hc-update-everything --help
to see all available options:
$ hc-update-everything --help
Usage: hc-update-everything [OPTIONS]
Options:
--system Run a system software update first
--brew-greedy-latest Also update brew casks with a :latest version tag
--flutter Also update Flutter and Dart
--help Show this help message and exit
In order to reclaim some disk space, you can occasionally clear caches from tools like brew and pip, or permanently clear unused data from tools like Docker by running the hc-reclaim-diskspace
helper:
hc-reclaim-diskspace
Take a look at this "awesome" list where you will find a lot of cool ways to personalise your Mac. My default settings can be found in the scripts/macos.zsh
script.
I'll highlight one important example here... From the macOS User Guide, you have the option to add a message on the Mac login window. It can be used "to provide contact information for a misplaced computer."
sudo defaults write /Library/Preferences/com.apple.loginwindow LoginwindowText "If lost, please contact your_email_here@example.com"
Here I'm using a oh-my-zsh configuration with a powerlevel10k theme. You can consider other frameworks, such as:
- prezto - The configuration framework for Zsh
- zinit - Ultra-flexible and fast Zsh plugin manager with clean fpath, reports, completion management, Turbo, annexes, services, packages.
- zplug - A next-generation plugin manager for zsh
- antibody - The fastest shell plugin manager.
The rules that define whether a startup script is sourced (and in which order) differ depending on the UNIX shell, initialization strategy, and even operating system. To keep things simple, we’ll focus only on the zsh shell on a macOS system.
- When opening a new terminal shell on a terminal emulator such as iTerm2 or Terminal, the following
files get sourced in the following order:
.zshenv --> .zprofile --> .zshrc --> .zlogin
. You are now using an interactive login shell. Once you kill the current shell, the.zlogout
script will be sourced before killing the process. - If you source a script (e.g.
source some_script
or. ./some_script
), no startup files get sourced. This is becausesource
reads and executes the contents of your script within the current shell environment. - If you run a script as an executable (e.g.
./some_script
), the script will run in a new shell. By default, and in most cases, this will be a non-interactive non-login shell and, therefore, only.zshenv
will be sourced before executing the script. You can extend this logic ad infinitum... if this script calls yet another script, which in turn calls yet again another script, etc, etc... each one will run in a new shell. Here I’m assuming that the script will be executed within a zsh shell, of course! If the script contains a#!/usr/bin/env zsh
shebang line, it can be executed directly as./some_script
or called as a regular command if it exists under$PATH
. Alternatively you can explicitly execute the script aszsh some_script
.
I use pyenv to manage multiple Python versions. I have also
defined some extra utilities under shell/functions/tau.zsh
to make it easier to install and
manage these multiple installations.
- Create Zsh functions
- Awesome dotfiles - A curated list of dotfiles resources.
- The "Hacker News Comment" Method - This Hacker News comment popularised the "bare repository and alias method" for managing dotfiles. This method is also references in Dotfiles (ArchWiki).
- Awesome macOS Command Line - inspiration for most of the settings in macos.zsh.
- A User's Guide to the Z-Shell - What to put in your startup files
- https://github.com/grant/new-computer-checklist
- https://github.com/unixorn/awesome-zsh-plugins
- https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
- https://github.com/iCHAIT/awesome-macOS
- A collection of useful .gitignore templates
- Shell startup scripts - An article about standardizing shell startup scripts.