<-

Dotfiles automation using Ansible

My environment is a combination of many tools and programs. Most of them are delicately customized for my needs. I always wanted to be able to take a new laptop and turn it into ready for use development machine automatically.

Table of Content

Package managers

The first step is to avoid manual installation. Downloading installer or building from source takes a lot of time and is harder to automate. Every modern operating system has package manager with all popular applications wrapped in ready for use packages which can be loaded via the client software.

Brew is the most popular package manager on macOS, Chocolatey on Windows, Pacman on arch Linux. These tools simplify the installation process and management of already installed apps.

Examples to get a taste of it:

Install Google Chrome:

brew cask install google-chrome

List all installed packages:

brew cask list

Upgrade Google Chrome to the last version:

brew upgrade google-chrome

Uninstall Google Chrome:

brew uninstall google-chrome

Dotfiles

Next step is avoiding manual configuration. Often apps sell you beautiful complex UI for configuration, where you waste hours configuring, the problem with that is an inability to synchronize settings on multiple devices.

What is very convenient is being able to serialize your preferences into a format that can be saved in the cloud and nothing is better suited for that than plain text files.

Dotfiles enable you to store configuration settings in a traditional text file in YAML or JSON format from where your tool will read it and function in the way you want.

Examples: .profile.clj, .tmux.conf, .vimrc, .zshrc.

Ansible

After it is clear how to install and configure applications, the next issue is how to automate that process and synchronize configurations, so that one line in the terminal can turn a fresh laptop into ready for a use development machine.

Bash scripts are usually used for that, although there are many small things you have to implement: variables management, templating, making sure the script is idempotent and so on. Alternately to bash scripts, I prefer to use Ansible.

Ansible is a famous tool in the DevOps community. It is largely applied to servers provisioning, for example, installing a database on EC2 machines in AWS, however, also works like charm to provision my personal laptop.

All setup instructions needed to turn my new MacBook into ready for use development machine are in the form of Ansible provision scripts and saved on Github in env-automation project.

Roles

Roles define application installation and configuration. Each role consists of:

  • tasks - actions neede to perform (brew install, create dir, copy file etc)
  • variables - configuration variables, lists etc
  • templates - file templates used to generate dotfiles, taking in account variables

For example tasks of Nvim role, install package from the brew and generates .nvimrc

# main.yml
---
- name: Make sure Nvim is present
homebrew:
name:
- neovim
state: present
- name: Make sure .nvimrc is present
template:
src: templates/.nvimrc
dest: ~/.nvimrc
".nvimrc
filetype indent plugin on "detect ft, load FTP file and indent
syntax on " syntax highlighting on
set langmenu=en_US
set hidden " be able to switch buffers without file save
set showcmd " shows the command in the last line
set nostartofline " some command move to the first non-blank line
set number " line number on
set clipboard=unnamedplus " allow copy-paste system <-> nvim

Playbook

Playbook defines which roles are going to be installed, here is the playbook that provisions my machine:

# playbook.yml
---
- hosts: local
vars_files:
- vars/variables.yml
roles:
- {role: 'macos', tags: 'macos'}
- {role: 'packages', tags: 'packages'}
- {role: 'git', tags: 'git'}
- {role: 'python', tags: 'python'}
- {role: 'ssh', tags: 'ssh'}
- {role: 'asdf-vm', tags: 'asdf-vm'}
- {role: 'zsh', tags: 'zsh'}
- {role: 'tmux', tags: 'tmux'}
- {role: 'vim', tags: 'vim'}
- {role: 'alacritty', tags: 'alacritty'}
- {role: 'aws', tags: 'aws'}
- {role: 'golang', tags: 'golang'}
- {role: 'jvm', tags: 'jvm'}
- {role: 'clojure', tags: 'clojure'}
- {role: 'docker', tags: 'docker'}
- {role: 'node', tags: 'node'}

Conclusion

When I get an entirely new machine I download env-automation from Github and use it to install all roles, special init.sh for that. It installs basic dependencies and runs Ansible in 20-30 minutes it automatically downloads and configures all needed software.

When I need to update some specific role, I change config files it in the project first and then install only specific role:

make install-only tags=zsh

This way my settings are always in sync and I am never scared that my carefully crafted setup can be lost.

Contribute on Github