NAME

update_config - Deploy configuration fragments (dotfiles, etc.) locally or on remote hosts.

SYNOPSIS

update_config [--test] [--help] [--diff] [--silent] [--list] [--dir directory] [--root|--no-root] [--export] [--pack]

DESCRIPTION

The program reads configuration fragments from the ~/.update_config directory if it exists. Otherwise, it reads them from the $XDG_CONFIG_HOME/update_config directory (~/.config/update_config when XDG_CONFIG_HOME is not set). In both cases, all the subdirectories are read too, as well as the fragments embedded in the script itself by the --pack option. A fragment from the directory replaces an embedded fragment with the same name.

OPTIONS

The available options for the program are the following. They can all be abbreviated down to a single character.

--diff   Print the difference between the current configuration and what
         will be written

--silent Do not print the status of the configuration files (except for
         error messages).

--test   Do not modify the configuration files.

--list   Only print a list of the configuration files and exit.

--root   Install only the configuration fragments that are marked as
         'NEED ROOT'.

--no-root  Do not attempt to install the 'NEED ROOT' fragments.

--export Export in the current directory all the files that are embedded
         in the script.

--dir directory   Process only the configuration files in the given
         directory and its subdirectories, instead of those from the
         default path.

--pack  Write on the standard output a version of the script where all the
        config fragments are embedded. To be used typically with:
          update_config --pack | ssh user@host perl -- - --no-root
        or just
          update_config --pack | ssh host perl

--help   Print this help message.

FRAGMENT FILES

Each fragment file describes a piece of content that should be present in a target file. By default, the target is a file directly in your home directory, with the same name as the fragment. However, hidden files are ignored when the fragments are read, so most of the time you will set the target explicitly with the FILENAME directive described below (e.g. to update your .bashrc).

You can organize the fragments in sub-directories, but these are not part of the target path. Only the base name of a fragment is used, so two fragments must not have the same name, even in different directories. To have several fragments for the same file, give them distinct names with a suffix between brackets (e.g. bashrc [linux] and bashrc [macos], both with FILENAME .bashrc). That suffix is ignored if the name of the fragment is used as the target. Hidden directories are ignored too and fragments are processed in alphabetical order, ignoring case.

The content of the fragment is written in the target file between two guard lines, which look like ###AUTO-CONFIG START### and ###AUTO-CONFIG END###. Everything outside of these guards is left untouched, so you can still edit the rest of the file by hand. If the guards are not already present, the content is added at the end of the file, which is created if needed. When the guarded content is already up to date, the file is not modified at all.

Directives

A fragment can start with a few directives, one per line, that control how it is applied. The content of the fragment starts at the first line that is not a directive, and the empty lines following the directives are skipped. The supported directives are the following:

FILENAME path

Sets the target file, instead of using the name of the fragment. The path is relative to your home directory, unless it is an absolute path. The path can also be made of two commands separated by a comma. In that case, the current content is read from the output of the first command and the new content is passed on the input of the second one (this is useful for crontab -l, crontab -).

COMMENTS CHAR char

Sets the character that starts a comment in the target file, which is used in the guard lines (it is repeated three times). The default is #.

GUARD SUFFIX suffix

Adds a suffix to the guard lines, which then look like ###AUTO-CONFIG:suffix START###. You need this if several fragments target the same file.

IS PREFIX

Adds the content at the beginning of the file instead of at the end, when the guards are not already present.

ENTIRE FILE

Replaces the entire target file with the content of the fragment, without any guard lines.

MAKE EXECUTABLE

Makes the target file executable.

PERM mode

Sets the permissions of the target file, given in octal (e.g. 600).

NEED ROOT

Marks a fragment that must be written as root (typically a file under /etc). These fragments are applied at the end of the run, by executing the program again through sudo with the --root option.

REQUIRE expression [# reason]

Evaluates a Perl expression and skips the fragment if it is false. The optional reason is printed when the fragment is skipped. The expression can use the which function from File::Which as well as the variables from English. This directive can be repeated, in which case all the expressions must be true.

POST UPDATE command

Executes a shell command after the target file has been updated. In the command, $@ is replaced by the path of the target file.

REM text

A comment, which is ignored.

Examples

This fragment adds some content to your ~/.bashrc file, but only on WSL hosts. The guard suffix allows to have another fragment for the same file:

FILENAME .bashrc
GUARD SUFFIX WSL
REQUIRE `uname -r` =~ /WSL/ # Not a WSL host

export BROWSER=wslview

This one installs a full script in a system directory, and needs root to do so:

FILENAME /etc/network/if-pre-up.d/10-iptables-load
REQUIRE -d '/etc/network/if-pre-up.d' # No if-pre-up.d directory
NEED ROOT
ENTIRE FILE
MAKE EXECUTABLE

#!/bin/sh
iptables-restore < /etc/iptables.rules

And this one updates a configuration file and then reloads it in the running program:

FILENAME .tmux.conf
REQUIRE which('tmux') # tmux is not installed
POST UPDATE tmux source-file $@

set -g prefix C-a