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 aliases and EXPORTs in the secrets/ dir, and my less interesting, non-secret aliases in system/.alias.

And that's it!

marcello

Resources