Managing Dotfiles with a Makefile

Assumed Knowledge: make, GPG, git.

Makefiles are great for managing dotfiles. They can do anything:

  • Copy standard configs.
  • Track changes.
  • Keep separate versions for each computer.
  • Securely handle secret files with passwords.
  • Handle crontab.

The Setup

Mine looks like this:

 1config/:
 2 aerc         cava       khard         rofi      tut            picom.conf
 3 alacritty    cmus       lf            s         vdirsyncer     powerbash.sh
 4 amfora       dunst      ncmpcpp       sc-im     waybar         powerbashrc
 5 bat          gtk-3.0    newsraft      sway      zathura        redshift.conf
 6 bottom       himalaya   procps        systemd   compton.conf
 7 bugwarrior   i3         profanity     task      mpd.conf
 8 calcurse     i3blocks   qutebrowser   tspreed   mpv.conf
 9
10home/:
11 gnupg   mozilla   vim   bash_profile   gitconfig          inputrc     xinitrc
12local   unison    w3m   bashrc         gitignore_global   tmux.conf   Xresources

Standard configs are made with hard links, rather than soft. No need to destroy files, replace them with soft links, then remove the soft links and replace them with copies, then back again.

Just make hard links, and every change to that file will be tracked in your dot-files repo.

Command-Based Files

Some files are usually created through commands, like ~/.gitconfig. These can change on different computers, as you add git lfs on one, or a credential helper on another. So if you don't want to keep them in sync, you can just put the universal commands into a Makefile. If your name ever changes, then all computers receive that change, but nothing will touch the strange git helpers installed on that old laptop.

1$(HOME)/.gitconfig: git.mk
2    git config --global user.name "$yerName"
3    [ ... ]

The same approach lets you manage secrets. For example, you can set the secret token value in glab (the Gitlab CLI tool):

1git_token != pass gitlab-token
2glab config set token "$(git_token)" --host gitlab.com

Secrets Management

You can use pass to manage secrets with make. For example, to keep ~/.config/aerc/accounts.conf as a secret file, just throw it into pass:

1cat ~/.config/aerc/accounts.conf | pass insert --echo config/aerc/accounts.conf

Add this file as a secret, and make a rule to create these secrets from all the files in ~/.password-store/:

1secrets += ${HOME}/.config/aerc/accounts.conf
2
3$(secrets): ${HOME}/.% : ${HOME}/.password-store/%.gpg
4    mkdir -p $(@D)
5    chmod 700 $(@D)
6	gpg --quiet --decrypt $< > $@ 
7	chmod 600 $@

Handling Crontab

Changes in different computers' crontab files can inflict a lot of merges. Avoid this by just making each set of tasks its own file:

1echo '@daily import_events.sh "https://dmz.rs/events.ical"' > extra/cron/calendar
2echo '@hourly mbsync -a' > extra/cron/email

Combine the lot with cat, then import this into cron:

1cron.txt: $(wildcard extra/cron/*)
2	cat $^ > $@
3    crontab $@

Example Repo

You can find my basic setup here:

1ssh -p 2222 soft.dmz.rs -t mkdots

Or just clone the repo and see how it works:

1git clone ssh://soft.dmz.rs:2222/mkdots .dots
2cd .dots
3make -n