Version controlled configs
Disclaimer: I typed this is a hurry and haven’t proof-read it, or tried running any of the code (except the script at the end).
So a while ago I decided to use git to track all my dot-files and other assorted configuration stuff that each of my linux systems need. I’m going to try to outline how I did this:
First, I have some files that exist on all my machines. These I’m going to call common
files. Things like muttrc
, irssi config scripts, etc. These are also mostly configuration I don’t mind making public.
Second, I have some stuff that are common, but need to be private. I call these private
files.
Lastly, I have files that are specific to a machine, but I still want tracked. Things like .xinitrc
and .profile
are in here.
First thing we did is create some directories:
mkdir -p .config.git/common
mkdir -p .config.git/private
So, all the common configuration stuff will go in ../common
, the ssh/gpg/other private things will go in ../private
, and the machine specific stuff will just go in .config.git
.
Next we create some repos.
git init .config.git/common
git init .config.git/private
git init .config.git
Excellent. Be careful with this next step, you’re about to not have any configuration.
cd $HOME
mv .bashrc
mv *rc .config.git/common
mv .ssh .config.git/private
mv .gnupg .config.git/private
mv .profile .config.git/
mv .config.git/common/.xinitrc .config.git/
Now we want to link all the configurations together using submodules. Git allows you to add one repository as a kind of dependency of another. This is called a submodule. You do this as follows:
cd .config.git
git submodule add $HOME/.config.git/common
git submodule add $HOME/.config.git/private
Alternatively, if you want to push these to your server first:
cd $HOME/.config.git/common
git add *
git commit -a -m "Init"
ssh user@myserver.com git init --bare ~/repos/config.common
git remote add origin myserver.com:~/repos/config.common
git push origin master
cd $HOME/.config.git/private
git add *
git commit -a -m "init"
ssh user@myserver.com git init --bare ~/repos/config.private
git remote add origin myserver.com:~/repos/config.private
git push origin master
cd $HOME/.config.git/
git add .profile
git add .xinitrc
git submodule add user@myserver.com:~/repos/config.common
git submodule add user@myserver.com:~/repos/config.private
git commit -a -m "init" ssh user@myserver.com
git init --bare ~/repos/config.machine-name
git remote add origin myserver.com:~/repos/config.machine-name
git push origin master
Now, you need to link everything back into your home so you can actually use your version controlled configs. I use a script to do this:
#/bin/bash
# A script for creating symlinks to all the dot files stored in the
# config.git repo.
CONFIG_PATHS=${CONFIG_PATHS:-"~/.config.git/common/ ~/.config.git/private ~/.config.git/"}
EXCLUDE=${EXCLUDE:-"$1 bin/lndot.sh common/? private/? .config.git/? .git"}
# Make sure to exclude ., .., and any other files listed in $EXCLUDE
FIND_CMD="find $CONFIG_PATHS -maxdepth 1 -iname \"*\" "
for file in $EXCLUDE
do
FIND_CMD=$FIND_CMD"| egrep -v \"$file\$\" "
done
echo $FIND_CMD
# Ensure we're in home, to make proper symlinks
pushd ~/ >/dev/null
for i in $(eval $FIND_CMD)
do
ln -f -s $i ~/
done
popd >/dev/null
Now, you have your configuration files version controlled, linked properly, and easily shareable! To add a new machine to this setup, all you need to do is:
git init .config.git
cd .config.git
git submodule add user@server.com:~/repos/config.common
git submodule add user@server.com:~/repos/config.private
ssh user@myserver.com git init --bare ~/repos/config.machine-name2
mv ~/.xinitrc .
mv ~/.profile .
git add .xinitrc
git add .profile
... etc
One important thing to note: whenever you change a file from a submodule, you should perform a commit/push of the parent module. Otherwise, when cloning the parent module, it will pull an older revision of the submodule.
The final piece of this setup, for me, was learning how to use bash configuration files so I could have both common and machine specific files coexist, and have settings that exist for all shells, or only interactive/login shells. I might make another post about this later.