Most of us use dotfiles to personalise our systems - and I'm no different. I've recently taken to automating away my management and testing of dotfiles by using GitHub, Travis CI and Docker.
Within the last year I finally got bored of manually copying and applying dotfiles between machines, or recreating them whenever I re-installed. My solution to this wasn’t revolutionary: I put my dotfiles in a repository.
Over the last year I’ve changed the way I manage my dotfiles quite a lot, including making my dotfiles repository public as I often share aspects of my setup with friends and colleagues.
If you don’t already keep your dotfiles in a repository, I’d recommend that you do. I’ve managed to automate most aspects managing, apply and testing my dotfiles. I no longer have to deal with inconsistencies in my setup or apply bad or incomplete dotfile changes.
Setting up a dotfiles repository
The first and obvious step is to create a repository and place your dotfiles in there.
Keep it organised
I’ve gotten to the point where I don’t often need to change my dotfiles and configuration. Keep my repository organised by tool or topic helps me find and manage the dotfiles and configuration files for different tools and utilities.
Use a setup script
Keep your dotfiles in a repository is a good first step. The next big time saver is being able to apply these dotfiles quickly and easily.
The way that I achieved this was with a single setup script that symlinks my dotfiles to the files in the repository.
Install tools and dependencies
Have your setup script install all common tools and dependencies you use all the time. Installing software is boring, so why not automate away that hassle.
I have a few scripts that I find useful to have on any machine. I happen to keep these in a scripts repository.
My scripts repository is setup as a submodule of my dotfiles repository. I also create symlinks in my local bin folder to the copy in the scripts submodule.
For a while I use to test my dotfiles setup using a VM with a clean operating system install. I had two problems with this approach:
- Testing was slow
- It required manual steps
Within the last couple of weeks I’ve transitioned to a process that now allows me to automatically test my dotfiles repository on every change, quickly, easily and without any manual steps.
My Dockerfile is quite simple, under a test user I run the setup script for my dotfiles repository which happens on a clean base image of the OS I am currently using:
FROM ubuntu:16.04 MAINTAINER James Ridgway # OS updates and install RUN apt-get -qq update RUN apt-get install git sudo zsh -qq -y # Create test user and add to sudoers RUN useradd -m -s /bin/zsh tester RUN usermod -aG sudo tester RUN echo "tester ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers # Add dotfiles and chown ADD . /home/tester/projects/dotfiles RUN chown -R tester:tester /home/tester # Switch testuser USER tester ENV HOME /home/tester # Change working directory WORKDIR /home/tester/projects/dotfiles # Run setup RUN ./setup CMD ["/bin/bash"]
Running the setup script is run as part of the docker build, so if setup fails docker will fail to build the container.
I can test my dotfiles locally by running:
docker build -t docker.james-ridgway.co.uk/dotfiles .
And if I want to inspect the state of the container after a successful setup, I can run the container to get a bash prompt:
docker run -it docker.james-ridgway.co.uk/dotfiles
Travis CI is used to build the docker container:
sudo: required services: - docker # Handle git submodules yourself git: submodules: false # Use sed to replace the SSH URL with the public URL, then initialize submodules before_install: - sed -i 'email@example.com:/https:\/\/github.com\//' .gitmodules - git submodule update --init --recursive script: - docker build .
.travis.yml file is fairly straightforward although I have added some special behaviour around submodules, as I use a submodule for my common scripts.
The default environment in which Travis will run your build won’t have access to your private key, so you won’t be able to clone submodules under SSH. The quick fix for this is to:
- Disable submodules
- Replace SSH clone URLs in .gitmodules with HTTPS clone URLs
And that's it!