Skip to content

Setup and Configuration

SuperCuber edited this page Jun 18, 2024 · 17 revisions

Setup

  • If you don't have a dotfile repository yet:
    • Figure out all the applications that use dotfiles that you want to keep track of, and write down where all those files are located.
    • Create a new git repo (I suggest placing it in ~/.dotfiles)
    • Move those dotfiles to the repo. You can also change the names to make sense to you - for example a file that was at ~/.i3/config can be renamed to simply i3.
    • Commit and push, you now have a dotfiles repo!
  • In your dotfiles repo, create a folder .dotter and two files: global.toml and local.toml.
  • Add .dotter/local.toml to your .gitignore - that file contains the machine-specific configuration, so there's no point uploading it.
  • Add .dotter/cache.toml and .dotter/cache to your .gitignore as well
  • When installing, I recommend downloading the binaries (windows and linux) into the root of your repository.
    That way, wherever your dotfiles are, Dotter also is.
  • On Linux, make sure dotter has execute permissions with chmod +x dotter, then you can run it with ./dotter
  • To get a starter configuration after your files are in the repository, use dotter init

Configuration

Dotter operates under a concept of "packages". This comes from the idea that you don't always want all your dotfiles to be deployed - sometimes only part of the programs are installed.

In global.toml, packages are defined. Which packages are deployed is configured in local.toml.

Here's an example global.toml, that showcases several features:

# Helpers are user-defined functions that can be executed inside templates.
# This section is optional.
[helpers]
color_hex2rgb = "dotter_settings/helpers/color_hex2rgb.rhai"

# A package contains two sections - "files" and "variables".
# Both of those sections are optional - you can have only one if you want.

# The 'files' section is a mapping between the path of the file relative to
# the repository root and its location in the filesystem (where the program
# expects it)
# In this case, say your repository is at `~/.dotfiles`, it will map
# `~/.dotfiles/zsh/zprofile` to `~/.zprofile`,
# and `~/.dotfiles/zshrc` to `~/.zshrc`
# To clarify, folders in the repository don't have to correspond to packages.
# On Windows, '~' is expanded to 'C:\Users\<USERNAME>\'
[zsh.files]
"zsh/zprofile" = "~/.zprofile"
zshrc = "~/.zshrc"

# The 'variables' section contains constants that the templated files
# can access. This section can contain all the types that toml supports,
# and is used by the Handlebars templating engine as the rendering context.
[zsh.variables]
prompt_color = "#00FF00"

# A package for all files relating to the i3 window manager
# I would only select it if I had i3 installed,
# so for example, I wouldn't select it on my VPS since it has no screen.

# The `depends` field means that when this package is enabled, all of the
# depended packages will be recursively enabled as well.
# As you can see, I also left a comment that I need to configure certain
# machine-specific variables if I want to use this package.

# Note that variables from a selected package are available to all others.
[i3]
depends = ["graphics"]

[i3.files]
Xinitrc = "~/.xinitrc"
i3 = "~/.i3/config"
polybar = "~/.config/polybar/config"
# Local variables: network_interface, screen_size, terminal

# A variables-only package, maybe it contains variables that are also
# used by my terminal so I want them to exist when I select either of
# the packages, without having to repeat them.
[graphics.variables]
font_size = 14
primary_color = "#CCCCCC"
background_color = "#333333"

As you can see, a global.toml contains a description of all the dotfiles that are possible to install.

But you don't always want all of them. Which packages are installed as well as machine-specific tweaks are configured in local.toml:

# local.toml can "include" another file - more on this in the next section.
includes = [".dotter/linux.toml"]

# An array of the names of all packages that are selected.
# Only the files and variables that belong to packages in this list are kept.
# Note that in this example, the `graphics` package is automatically added as well.
packages = ["i3"]

# File target locations can be overridden in local.toml
# This can be useful for example for applications which read from a different
#  location depending on the platform.
# Disabling files is possible by setting them to the special value "".
[files]
Xinitrc = "~/.my_Xinitrc"
polybar = ""

# I need to define some machine-specific variables.
[variables]
network_interface = "wlan0"
screen_size = "1920x1080"
terminal = "xfce4-terminal"
# Actually, I want the font size on this screen to be a bit bigger.
# Any variables defined in local.toml override variables in global.toml.
# Unlike files, it's impossible to delete variables.
font_size = 18

For the initial configuration, you might want to have a single default package that contains just a files section, then select it in local.toml. You can always break it up into smaller packages later!

Hostname local toml

If local.toml file is not found, Dotter will look for a file named <hostname>.toml using the machine's hostname. If you're not sure what file this is looking for, then:

  • Make sure there is no local.toml
  • Run dotter -v
  • Look for the message saying local.toml not found, using SOMETHING.toml instead (based on hostname)

Included files and configuration merging

In local.toml, you can specify a list of files to include. included.toml looks similar to a global.toml, but with only packages:

[zsh.files]
zshrc = "~/.another_location"

[zsh.variables]
prompt_color = "#0000FF"

The files are loaded and merged in the following order:

  • First, all the packages from global.toml are loaded
  • Then, each package is merged with the included files in order
    • The last time each key is set is the one that will win. So included.toml will override a file or variable set in global.toml
  • Then, only the packages that are in local.toml's packages variable and their dependencies are kept
  • Then, all the packages are merged into one - at this point, there mustn't be any file/variable clashes between the packages
  • Then, the merged "package" is once again merged with local.toml's contents - local.toml wins any clashes.

Why is this useful? Say, for example, you want an OS-specific configuration - maybe a program reads a file from a different location depending on the OS. (I'll use neovim as an example)

In that case, you could define global.toml like this:

[vim.files]
# This is the location for Linux
vimrc = "~/.config/nvim/init.vim"

windows.toml like this:

[vim.files]
# This is the location for Windows
vimrc = "~/AppData/Local/nvim/init.vim"

And then, on Windows you could include windows.toml like this:

includes = [".dotter/windows.toml"]
packages = ["vim"]

This will load the default linux location, then override it with the windows location, and then only deploy the file if you've selected the package.
windows.toml could contain the windows-specific tweaks of all of your packages.
If you wanted some of the settings from windows.toml but then override the rest you could do that as well - the possibilities are limitless.

Manual patch

When using the -p/--patch flag, standard input will be taken as an additional step on top of local.toml's [files] and [variables] - use this for one-time playing with the configuration, for example

dotter -p <<<"variables.font_size = 20"

This is using bash's here-string. Other shells have similar features allowing this as well (see also heredoc)

Settings

Dotter allows you to configure some global settings.

There is currently only one setting, but if you have an idea, please open an issue.

default_target_type options: symbolic, template, or automatic defaults to automatic

Sets the default target type for any package files that don't explicity set a target type.

In global.toml:

[settings]
default_target_type = "symbolic"

[nvim]
depends = []

[nvim.files]
nvim = "~/.config/nvim" # Does not detect anymore, will always be symbolic

Builtin variables

Dotter automatically defines the built-in variable dotter, that is a mapping including the following:

  • dotter.packages - a mapping between the package name and the value true of the selected packages, intended to be used like {{#if dotter.packages.my_package}} (this will evaluate to false for packages that aren't in the mapping)
  • dotter.files - a mapping between the source and target of each deployed file
  • dotter.os - either windows or unix, use something like {{#if (eq dotter.os "unix")}}
  • dotter.current_dir - contains the absolute path to the directory Dotter was ran from (usually the root of the repository)
  • More will be added eventually - if you have an idea, please open an issue.

Complex target

Dotter also supports configuring targets of files using this syntax:

[zsh.files]
zshrc = { target = "~/.zshrc", type = "symbolic" }
zprofile = { target = "~/.zshrc", type = "template" }

Use this to override Dotter's default detection behavior, where it checks whether the file contains {{ to see if it's a template or a symlink.

There is also an owner field - it can either be an integer for a UID or a string for a username. To set the owner, Dotter will use sudo to request elevation.

When type = "template", Dotter also supports additional arguments:

[zsh.files]
zprofile = { target = "~/.zshrc", type = "template", append = "text to append", prepend = "text to prepend" }

This can be useful in certain cases in local.toml.

An alternative syntax for this is:

[zsh.files.zprofile]
target = "~/.zshrc"
type = "template"
append = """
I can use
multiline text
here!
"""

TOML actually sees those two as equivalent, the difference is purely cosmetic.

Conditional file

If you want to control whether a file is included based on the result of a helper or a variable, use the if field, like so:

[zsh.files.zprofile]
target = "~/.zshrc"
type = "symbolic"
if = "bash"
# This expression is evaluated just like the argument of an {{#if}}
if = "(eq shell 'bash')"

Example

For an example of a repository that uses Dotter, check out my dotfiles. The folder of interest is .dotter.

If you want your repository to be mentioned here, feel free to open an issue :)