One of my guilty pleasures is maintaining my dotfiles. This is one project I can yak-shave and play with as much as I want without caring about the actual impact or in general the usefulness of the whole endeavour. So this weekend I decided to indulge myself.
Part of the process was cleaning up the older dotfiles and updating them to
newer preferences. And the other part (which is the subject of this blog post)
was the installation process, which is basically the process of how these files
get from my dotfiles
repository to my home folder.
There are many different ways you can do this. So far, I've been using GNU Stow for this purpose.
GNU Stow
Stow is a useful little program to help you manage symbolic links.
Given a source directory and a target directory, it is able to add symbolic links into the target directory that point back to the source. So if you're maintaining your dotfiles in a git repository and want to have your home folder link back to them, Stow is perfect for this.
As an example, assume your dotfiles
project contains a nvim
folder for your
Neovim configuration files and that it should be linked to ~/.config/nvim
,
Stow can do that for you if you run stow nvim --target $HOME/.config
.
If you maintain multiple dotfiles, your setup process might end up in a shell script resembling the following:
mkdir -p ~/.config/kitty
stow kitty --target $HOME/.config
mkdir -p ~/.config/nvim
stow nvim --target $HOME/.config
stow zsh --target $HOME/
While this does the job, the one thing that never really sat well with me (no actual impact, remember?) was the fact that it's a shell script.
Generally speaking, I'm allergic to shell scripts. I don't write them on a regular basis, so when I do have to work with one, I constantly need to look up the documentation of the most primitive things (eg. "bash if else syntax") which is really unproductive. Debugging shell scripts is also not the most fun activity. So in general, I try to avoid them as long as I can.
Dotbot
This weekend I was looking for a dotfiles manager (again, no actual impact) and found dotbot.
Dotbot is a dotfile bootstrapper that takes in a (YAML) configuration file that describes the symbolic link definitions (and some other metadata) and updates those definitions on disk. The functionality it offered looked almost exactly like what I wanted so I decided to give it a try.
The installation feels a bit clunky (the default instructions recommend
installing it as a git submodule) but luckily it's published on PyPI as a
package so we can use pipx
to install it.
$ pipx install dotbot
The next step was to remove the stow
invocations in favor of a dotbot
configuration file. The most recent version of my config file is available
here, but essentially it looks something like this:
- defaults:
link:
relink: true
- link:
~/.config/kitty/kitty.conf: kitty/kitty.conf
# ...
~/.zshrc: zsh/zshrc
- create:
- ~/.config/kitty
The defaults
section contains some default configuration options which I'm
not going to write about here as the README does a much better job of explaining
what it is for.
The real work happens inside link
, which is basically a key/value map of the
dotfile's destination and the source. This is where you can tell dotbot what
should be linked and where. Lastly, the create
section contains a list of
directories that should be created prior to any linking taking place.
With this place, dotbot can be invoked on the command line to set up all the symbolic links in one go:
$ dotbot --config-file install.conf.yaml
Link exists ~/.config/kitty/kitty.conf -> /home/siddhant/Work/projects/dotfiles/kitty/kitty.conf
...
Link exists ~/.zshrc -> /home/siddhant/Work/projects/dotfiles/zsh/zshrc
All links have been set up
Path exists /home/siddhant/.config/kitty
...
All paths have been set up
==> All tasks executed successfully
This feels much cleaner than the original shell script. Of course, given how many dotfiles I have (which is not that many), the original script would have sufficed. But as I mentioned before, none of this is an exercise in usefulness, so I personally consider this change perfectly acceptable. 🌝
Encrypted files
The one thing I'm missing from dotbot is support for encrypted files. It would be nice if you could add encrypted files to the git repository and have dotbot decrypt them automatically before putting them in place.
Note that there is a shell
directive for running shell commands. So in
theory, we could add a shell command for decrypting a sensitive file before the
symbolic links are established. Unfortunately, it seems like these commands are
run after the linking has been done, while we want the decryption to happen
before.
This means that I've been forced to add back a setup.sh
file which looks like
this:
# decrypt crestic password
age --decrypt --identity ~/.age/keys.txt --output crestic/password crestic/password.enc
# install
dotbot --config-file install.conf.yaml
The first line uses age
to decrypt a password file and put it in a location
where dotbot can read it and eventually place the link in the right location.
This is less than ideal, but it works.
Conclusion
Overall I have really come to like dotbot. It does everything I need it to do and some more.
There is also support for plugins and there are a bunch of nice plugins out
there as well. For instance, you can manage your crontab
schedule or specify a
list of apt
/snap
packages to be installed automatically. While not a strict
necessity, it's nice to know that these things are there.
In any case, I do see myself using dotbot going forward. At least until the next time I feel the need to yak-shave.