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:
FILENAMEpath-
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 CHARchar-
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 SUFFIXsuffix-
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.
PERMmode-
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
sudowith the--rootoption. REQUIREexpression [#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
whichfunction 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 UPDATEcommand-
Executes a shell command after the target file has been updated. In the command,
$@is replaced by the path of the target file. REMtext-
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