Today's simple tutorial: getting your dotfiles in order!
Note: This post was edited and improved in Dec 2020.
What is dotfiles, precious?
Dotfiles are:
- Files that start with
.
- And are technically hidden from you (i.e. they won't show up in Finder or with simple
ls
) - But you can actually just see them if you do
ls -a
- You end up fiddling with them to configure your system when you use the shell a lot
Probably the most popular dotfile you'll run into is .bash_profile
. This configures your shell (if you're using bash, which - by default - you probably are). But you can have a bunch of other dotfiles - for example, .pgpass
(where your Postgres credentials go), .id_rsa
(where your SSH keys are stored - which, incidentally, sits in a dotfiled directory, .ssh/
), and so on.
Anyway, I recently made a few dotfiles that I wanted to version control - specifically, my zsh and ipython setups.
Making your own dotfiles
You can see my completed dotfiles repo here.
There's a bunch of dotfile guides on the web (see below). My process was Simple Angela's Version. Namely, in my shell, first I made my dotfiles folder and got it ready for git
:
mkdir ~/.dotfiles
cd ~/.dotfiles
mkdir secrets
echo 'secrets/' >> .gitignore
mkdir git
mkdir jupyter
mkdir system
git init
git add .
git commit -m "Dotfiles begin..."
In other words, from the command line, I made the following directory structure:
- .dotfiles/
- git/
- jupyter/
- secrets/
- system/
- secrets/
- .gitignore
And I made sure to .gitignore
anything that shows up in .dotfiles/secrets/
, since that's where I would put any sensitive passwords, URLs, and API keys.
Now it was just a process of making the relevant dotfiles for each thingie (e.g. my system/
dotfiles would be stuff like my shell configs, git/
would have a global .gitignore
) and make sure that each thingie could find its config. Let's go through it, thingie by thingie:
Thingie 1: Jupyter
I've already written about my default IPython startup. But how to get IPython to find it? After all, IPython startup files live in ~/.ipython/profile_default/startup/
. The easiest way is to link the file to your .dotfiles
repo:
ln -s ~/.ipython/profile_default/startup/00-dotfiles.py ~/.dotfiles/jupyter/startup.py
Thingie 2: git
I used gibo
to quickly create a giant .global_gitignore
boilerplate to handle all my usual use-cases: Python, PyCharm, Sublime.
cd ~/.dotfiles/git
gibo Python macOS JetBrains SublimeText >> .global_gitignore
But now how to tell git about it?
Easy! Git has a global config file, stored in your $HOME
directory, that looks for a global .gitignore
and implements it. (Note that your .gitignore
file can't unfurl $
bashy things, so you have to hard-code the full path to your dotfiles.
[core]
editor = /Applications/Sublime\\ Text.app/Contents/SharedSupport/bin/subl -n -w
excludesfile = path/to/home/dir/.dotfiles/git/.global_gitignore
[user]
name = Angela Ambroz
email = angelaambroz@users.noreply.github.com
[alias]
unstage = reset HEAD --
Thingie 3: zsh
And finally, the majesty - the cherry on top - the crown jewel: zsh
! How was I going to tell my shell to, well, SHELL RIGHT?! To use this wonderful new dotfiles directory? This was two parts: having .zshrc
(z-shell startup file) loop and source through all the relevant dotfiles, and making sure my shell knew where to find the z-shell startup file.
For the latter: I learned about symbolic links (aka, symlinks
) using the ln
utility:
❯ tldr ln
ln
Creates links to files and folders.
- Create a symbolic link to a file (or folder):
ln -s path/to/file path/to/symlink
- Overwrite an existing symbolic to point to a different file:
ln -sf path/to/new_file path/to/symlink
- Create a hard link to a file:
ln path/to/file path/to/hardlink
(Side note but tldr
- i.e. plain English man
pages - is a lifesaver. I doff my hat.)
Okey dokey:
ln -s ~/.dotfiles/system/.zshrc ~/.zshrc
It's important to include that -s
(symbolic) flag, otherwise you'll just be linking the file once and updates won't be reflected.
And you can double check that the symlink was truly linked with a quick grep:
ls -a | grep '\->'
For the former (looping through the relevant dotfiles), I just added the following to ~/.dotfiles/system/.zshrc
:
# Load all my secrets
SECRETS="$HOME/.dotfiles/secrets"
for file in "$SECRETS"/.*
do
source "$file"
done
echo "Loaded secrets."
# Load non-secrets
source "$HOME/.dotfiles/system/.alias"
echo "Loaded non-secrets."
Since I broke out my keys et al. into alias
es and EXPORT
s in the secrets/
dir, and my less interesting, non-secret alias
es in system/.alias
.
And that's it!