My Own Configuration Manager


I have been using Linux since I was in my second year of undergraduate. My experiments with the dotfiles (configuration files) also started at the same time. For the uninformed, in Linux, it is common to configure a lot of settings and configurations within dotfiles. Dotfiles are files in a Linux user's home directory that begin with a dot or a full-stop character. This dot indicates to the operating system that these files are used to store the settings of programs like vim or shells like bash or fish to name a few.

In the beginning, I was keeping a manual backup of my dotfiles by copying them to a folder from time to time. But the list soon started getting huge, that it became complicated for me to keep track of the changes. Then I moved to symlinks. I started symlinking all the dotfiles from my folder to their usual locations. This setup worked perfectly fine, but as my collection of dotfiles grew, It became very cumbersome for me to symlink every dotfile manually.

I also tried a few tools built for this particular purpose. Some of them are vcsh, mr, and stow. These tools work just fine, but I was not willing to learn new tools just for maintaining my dotfiles. At last, I decided to write my tool to solve this problem. This way, there will not be any external dependency, and this tool will also become part of my dotfiles.

Design

The tool, in itself, is inspired by the UNIX tradition of keeping configuration files for the settings of the programs. This configuration system uses a JSON formatted dotfile.

Here is the source code for the configuration system. Let's have a look at the file structure of the repository.

|-- .backups
|   |-- 08-01-2018-15:47
|   |-- 08-01-2018-19:30
|   |-- ......
|-- configure.py
|-- current_status
|-- dotfiles
|   |-- dunst
|   |-- gtk-3.0
|   |-- i3
|   |-- ......
|-- dotfiles.json
|-- LICENSE
|-- README.md

During the initial setup, you need to edit the dotfiles.json file to suit your setup. A relevant section of the JSON file is given below.

{
  "pre": [
    {
      "name": "cloning repository",
      "command": "git",
      "subcommand": "clone",
      "argument": "https://github.com/yashhere/dotfiles.git"
    }
  ],
  "linking": [
    {
      "name": "bashrc",
      "src": "dotfiles/.bashrc",
      "dest": ".bashrc"
    },
    {
      "name": "bash_profile",
      "src": "dotfiles/.bash_profile",
      "dest": ".bash_profile"
    },
    {
      "name": "profile",
      "src": "dotfiles/.profile",
      "dest": ".profile"
    },
    {
      "name": "i3",
      "src": "dotfiles/i3",
      "dest": ".config/i3"
    }
  ]
}

As can be seen, the JSON file has an array variable linking, which can be used to set the paths for each configuration file and folder. The configure.py script also requires a dotfiles folder to be present in the current directory. The folder can be created manually, or if it is already version controlled on GitHub, then the script can clone it. For that, you can edit the pre section in the dotfiles.json.

Your dotfiles and config folders go inside the dotfiles folder. You need to copy all your current configurations to this folder to get started.

So, how does the script know where a file or folder will be linked? Simple, you need to edit the dotfiles.json file and add source and destination locations. For example, if you want to set up configurations of i3 to its original location (which is, $HOME/.config/i3), then you need to create a new JSON object in the linking array, like this.

{
  "name": "i3",
  "src": "dotfiles/i3",
  "dest": ".config/i3"
}

Here the name is used to identify the configuration file, the src parameter is the location of your config file/folder in the dotfiles directory, and the dest parameter is the final destination of the file/folder. Keen observers would notice that I have not used $HOME anywhere. It is understood that the configuration will go to the current user's home directory. So the dest is relative to the user's home directory, and src is relative to the directory from which the configure.py script is executed.

And you are done! Now, run configure.py, and all your dotfiles and folders will be symlinked to their correct place.

The current_status file saves all the symlink locations that are being managed by the script, for your easy reference and to debug any error.

Behind the Scenes

A lot to cool things happen behind the scenes. The script will check if any previous symlink exists at the given dest location. It removes any symlinks to avoid redundancy. If the dest already has any dotfile or folder, then it backs it up in the .backups under today's date and time before replacing it with a symlink to avoid any potential data loss.

I hope the article was useful. Cheers 😄