Save your dotfiles once, deploy them everywhere
Dotdrop makes the management of dotfiles between different hosts easy. It allows to store your dotfiles on git and automagically deploy different versions on different setups.
For example you can have a set of dotfiles for your home laptop and a different set for your office desktop. Those sets may overlap and different versions of the same dotfile can be deployed on different predefined profiles. Another use case is when you have a main set of dotfiles for your everyday's host and a sub-set you only need to deploy to temporary hosts (cloud VM, etc) that may be using a slightly different version of some of the dotfiles.
Features:
- Sync once every dotfile on git for different usages
- Allow dotfiles templating by leveraging jinja2
- Comparison between local and stored dotfiles
- Handling multiple profiles with different sets of dotfiles
- Easy import dotfiles
- Handle files and directories
- Associate an action to the deployment of specific dotfiles
Check the blog post and and the example for more.
Quick start:
mkdir dotfiles && cd dotfiles
git init
git submodule add https://github.com/deadc0de6/dotdrop.git
./dotdrop/bootstrap.sh
./dotdrop.sh --help
There exist many tools to manage dotfiles however not many allow to deploy different versions of the same dotfile on different hosts. Moreover dotdrop allows to specify the set of dotfiles that need to be deployed on a specific profile.
See the example for a concrete example on why dotdrop rocks.
Table of Contents
There's two ways of installing and using dotdrop, either as a submodule to your dotfiles git tree or system-wide through pypi.
Having dotdrop as a submodule guarantees that anywhere your are cloning your dotfiles git tree from you'll have dotdrop shipped with it. It is the recommended way.
The following will create a repository for your dotfiles and keep dotdrop as a submodules:
$ mkdir dotfiles; cd dotfiles
$ git init
$ git submodule add https://github.com/deadc0de6/dotdrop.git
$ ./dotdrop/bootstrap.sh
$ ./dotdrop.sh --help
Then install the requirements:
$ sudo pip3 install -r dotdrop/requirements.txt
For MacOS users, make sure to install realpath
through homebrew
(part of coreutils).
Using this solution will need you to work with dotdrop by
using the generated script dotdrop.sh
at the root
of your dotfiles repository.
Finally import your dotfiles as described below.
Start by installing dotdrop
$ sudo pip3 install dotdrop
And then create a repository for your dotfiles
$ mkdir dotfiles; cd dotfiles
$ git init
To avoid the need to provide the config file path to dotdrop each time it is called, you can create an alias:
alias dotdrop='dotdrop --cfg=<path-to-your-config.yaml>'
Replace any call to dotdrop.sh
in the documentation below
by dotdrop
if using the pypi solution.
Finally import your dotfiles as described below.
If starting fresh, the import
command of dotdrop
allows to easily and quickly get a running setup.
Install dotdrop on one of your host and then import any dotfiles you want dotdrop to manage (be it a file or a directory):
$ dotdrop.sh import ~/.vimrc ~/.xinitrc
Dotdrop does two things:
- Copy the dotfiles in the dotfiles directory
- Create the entries in the config.yaml file
Commit and push your changes.
Then go to another host where your dotfiles need to be managed as well, clone the previously setup git tree and compare local dotfiles with the ones stored by dotdrop:
$ dotdrop.sh list
$ dotdrop.sh compare --profile=<other-host-profile>
Then adapt any dotfile using the template feature and set a new profile for the current host by simply adding lines in the config files, for example:
...
profiles:
host1:
dotfiles:
- f_vimrc
- f_xinitrc
host2:
dotfiles:
- f_vimrc
...
When done, you can install your dotfiles using
$ dotdrop.sh install
That's it, a single repository with all your dotfiles for your different hosts.
For additional usage see the help:
$ dotdrop.sh --help
_ _ _
__| | ___ | |_ __| |_ __ ___ _ __
/ _` |/ _ \| __/ _` | '__/ _ \| '_ |
\__,_|\___/ \__\__,_|_| \___/| .__/
|_|
Usage:
dotdrop install [-fndV] [-c <path>] [-p <profile>]
dotdrop compare [-V] [-c <path>] [-p <profile>] [--files=<files>]
dotdrop import [-ldV] [-c <path>] [-p <profile>] <paths>...
dotdrop listfiles [-V] [-c <path>] [-p <profile>]
dotdrop list [-V] [-c <path>]
dotdrop --help
dotdrop --version
Options:
-p --profile=<profile> Specify the profile to use [default: thor].
-c --cfg=<path> Path to the config [default: config.yaml].
--files=<files> Comma separated list of files to compare.
-n --nodiff Do not diff when installing.
-l --link Import and link.
-f --force Do not warn if exists.
-V --verbose Be verbose.
-d --dry Dry run.
-v --version Show version.
-h --help Show this screen.
For easy deployment the default profile used by dotdrop reflects the hostname of the host on which it runs.
The config file (defaults to config.yaml) is a yaml file containing the following entries:
-
config entry: contains settings for the deployment
backup
: create a backup of the dotfile in case it differs from the one that will be installed by dotdropcreate
: create directory hierarchy when installing dotfiles if it doesn't existdotpath
: path to the directory containing the dotfiles to be managed by dotdrop (absolute path or relative to the config file location)
-
dotfiles entry: a list of dotfiles
- When
link
is true, dotdrop will create a symlink instead of copying. Template generation (as in template) is not supported whenlink
is true. actions
contains a list of action keys that need to be defined in the actions entry below.
- When
<dotfile-key-name>:
dst: <where-this-file-is-deployed>
src: <filename-within-the-dotpath>
# Optional
link: <true|false>
actions:
- <action-key>
- profiles entry: a list of profiles with the different dotfiles that
need to be managed
dotfiles
: the dotfiles associated to this profileinclude
: include all dotfiles from another profile (optional)
<some-name-usually-the-hostname>:
dotfiles:
- <some-dotfile-key-name-defined-above>
- <some-other-dotfile-key-name>
- ...
# Optional
include:
- <some-other-profile>
- ...
- actions entry: a list of action
<action-key>: <command-to-execute>
Simply run
$ dotdrop.sh install
Use the --profile
switch to specify a profile if not using
the host's hostname.
Compare local dotfiles with dotdrop's defined ones:
$ dotdrop.sh compare
Dotdrop allows to import dotfiles directly from the filesystem. It will copy the dotfile and update the config file automatically.
For example to import ~/.xinitrc
$ dotdrop.sh import ~/.xinitrc
$ dotdrop.sh list
Dotdrop allows to choose which profile to use with the --profile switch if you use something else than the default (the hostname).
The following command lists the different dotfiles configured for a specific profile:
$ dotdrop.sh listfiles --profile=<some-profile>
For example:
Dotfile(s) for profile "some-profile":
f_vimrc (file: "vimrc", link: False)
-> ~/.vimrc
f_dunstrc (file: "config/dunst/dunstrc", link: False)
-> ~/.config/dunst/dunstrc
It is sometimes useful to execute some kind of action
when deploying a dotfile. For example let's consider
Vundle is used
to manage vim's plugins, the following action could
be set to update and install the plugins when vimrc
is
deployed:
actions:
vundle: vim +VundleClean! +VundleInstall +VundleInstall! +qall
config:
backup: true
create: true
dotpath: dotfiles
dotfiles:
f_vimrc:
dst: ~/.vimrc
src: vimrc
actions:
- vundle
profiles:
home:
dotfiles:
- f_vimrc
Thus when f_vimrc
is installed, the command
vim +VundleClean! +VundleInstall +VundleInstall! +qall
will
be executed.
If used as a submodule, update it with
$ git submodule foreach git pull origin master
$ git add dotdrop
$ git commit -m 'update dotdrop'
$ git push
Through pypi:
$ sudo pip3 install dotdrop --upgrade
To use all defined dotfiles for a profile, simply use
the keyword ALL
.
For example:
dotfiles:
f_xinitrc:
dst: ~/.xinitrc
src: xinitrc
f_vimrc:
dst: ~/.vimrc
src: vimrc
profiles:
host1:
dotfiles:
- ALL
host2:
dotfiles:
- f_vimrc
If one profile is using the entire set of another profile, one can use
the include
entry to avoid redundancy.
For example:
profiles:
host1:
dotfiles:
- f_xinitrc
include:
- host2
host2:
dotfiles:
- f_vimrc
Here profile host1 contains all the dotfiles defined for host2 plus f_xinitrc
.
Dotdrop leverage the power of jinja2 to handle the templating of dotfiles. See jinja2 template doc or the example section for more information on how to template your dotfiles.
Note that dotdrop uses different delimiters than jinja2's defaults:
- block start =
{%@@
- block end =
@@%}
- variable start =
{{@@
- variable end =
@@}}
- comment start =
{#@@
- comment end =
@@#}
{{@@ profile @@}}
contains the profile provided to dotdrop. Below example shows how it is used.
It's possible to access environment variables inside the templates. This feature can be used like this:
{{@@ env['MY_VAR'] @@}}
This allows for storing host-specific properties and/or secrets in environment variables.
You can have an .env
file in the directory where your config.yaml
lies:
## My variables for this host
var1="some value"
var2="some other value"
## Some secrets
pass="verysecurepassword"
Of course, this file should not be tracked by git (put it in your .gitignore
).
Then you can invoke dotdrop with the help of an alias like that:
## when using dotdrop as a submodule
alias dotdrop='eval $(grep -v "^#" ~/dotfiles/.env) ~/dotfiles/dotdrop.sh'
## when using dotdrop from pypi
alias dotdrop='eval $(grep -v "^#" ~/dotfiles/.env) dotdrop --cfg=~/dotfiles/config.yaml'
This loads all the variables from .env
(while omitting lines starting with #
) before calling dotdrop.
Let's consider two hosts:
- home: home computer with hostname home
- office: office computer with hostname office
The home computer is running awesomeWM and the office computer bspwm. The .xinitrc file will therefore be different while still sharing some lines. Dotdrop allows to store only one single .xinitrc but to deploy different versions depending on where it is run from.
The following file is the dotfile stored in dotdrop containing jinja2 directives for the deployment based on the profile used.
Dotfile <dotpath>/xinitrc
:
#!/bin/bash
# load Xresources
userresources=$HOME/.Xresources
if [ -f "$userresources" ]; then
xrdb -merge "$userresources" &
fi
# launch the wm
{%@@ if profile == "home" @@%}
exec awesome
{%@@ elif profile == "office" @@%}
exec bspwm
{%@@ endif @@%}
The if branch will define which part is deployed based on the hostname of the host on which dotdrop is run from.
And here's how the config file looks like with this setup. Of course any combination of the dotfiles (different sets) can be done if more dotfiles have to be deployed.
config.yaml
file:
config:
backup: true
create: true
dotpath: dotfiles
dotfiles:
f_xinitrc:
dst: ~/.xinitrc
src: xinitrc
profiles:
home:
dotfiles:
- f_xinitrc
office:
dotfiles:
- f_xinitrc
Installing the dotfiles (the --profile
switch is not needed if
the hostname matches the entry in the config file):
# on home computer
$ dotdrop.sh install --profile=home
# on office computer
$ dotdrop.sh install --profile=office
Comparing the dotfiles:
# on home computer
$ dotdrop.sh compare
# on office computer
$ dotdrop.sh compare
For more examples, see how people are using dotdrop:
- https://github.com/open-dynaMIX/dotfiles
- https://github.com/moyiz/dotfiles
- https://github.com/japorized/dotfiles
These are some dotfiles related projects that have inspired me for dotdrop:
- https://github.com/EvanPurkhiser/dots
- https://github.com/jaagr/dots
- https://github.com/anishathalye/dotbot
- https://github.com/tomjnixon/Dotfiles
See also github does dotfiles
Initially dotdrop was used as a submodule directly in the dotfiles git tree. That solution allows your dotfiles to be shipped along with the tool able to handle them. Dotdrop is however also directly available on pypi.
If you want to keep it as a submodule (recommended), simply do the following
$ cd <dotfiles-directory>
## get latest version of the submodule
$ git submodule foreach git pull origin master
## and stage the changes
$ git add dotdrop
$ git commit -m 'update dotdrop'
## update the bash script wrapper
$ ./dotdrop/bootstrap.sh
## and stage the change to the dotdrop.sh script
$ git add dotdrop.sh
$ git commit -m 'update dotdrop.sh'
## and finally push the changes upstream
$ git push
Otherwise, simply install it from pypi as explained above and get rid of the submodule:
- move to the dotfiles directory where dotdrop is used as a submodule
$ cd <dotfiles-repository>
- remove the entire
submodule "dotdrop"
section in.gitmodules
- stage the changes
$ git add .gitmodules
- remove the entire
submodule "dotdrop"
section in.git/config
- remove the submodule
$ git rm --cached dotdrop
- remove the submodule from .git
$ rm -rf .git/modules/dotdrop
- commit the changes
$ git commit -m 'removing dotdrop submodule'
- remove any remaining files from the dotdrop submodule
$ rm -rf dotdrop
- remove
dotdrop.sh
$ git rm dotdrop.sh
$ git commit -m 'remove dotdrop.sh script'
- push upstream
$ git push
If you are having trouble installing or using dotdrop, open an issue.
If you want to contribute, feel free to do a PR (please follow PEP8).
This project is licensed under the terms of the GPLv3 license.