Framework for installing "dotfiles" packages into your environment
.
-filesNote: This repository contains the management code for installing packages, and not the actual "dotfiles" themselves. See Dotfiles.
dotfiles repositories are commonly used for synchronising files related to *nix shell environments. In addition to offering a synchronisation source, this piece of software also helps set up some more sought utilities and tools.
usage: dotfiles [-h] [-l | -i | -u] [package [package ...]]
The dotfiles.py
main script handles installing the environment.
Specify the packages to install.
If no packages are specified, the list and description of packages will be shown. If package names are specified, the default action is to install the packages.
Listing status for a particular package is possible if --list
is explicitly
specified: dotfiles --list foo bar
.
⚠️ Note: This tool is intended to be used when a new user profile is created, such as when a new machine is installed.
Multiple packages belonging to a package "group" can be selected by saying
group.*
or group.__ALL__
.
Multiple package sources can be given to Dotfiles. To edit the package configuration, use the built-in editor by calling
dotfiles --edit-sources
which will allow you to manually add, remove, move the sources.
The package sources are forming a priority list.
When installing a package foo
, it will be installed from the first source it
is found.
By default, the manager framework will use the following 2 package sources, in order:
~/Dotfiles/packages/
directory.To uninstall packages, specify --uninstall
before the package names:
dotfiles --uninstall foo
Packages are present across a number of source root directories, where an arbitrary hierarchy can be present.
A package is any directory which contains a (valid) package.json
file.
Subpackages are translated from filesystem hierarchy to logical hierarchy via
.
, i.e. tools/system/package.json
denotes the tools.system
package.
Package descriptor files are YAMLs which contain the directives describing installation and handling of the package. Any other file is disregarded by the tool unless explicitly used, e.g. being the source of a copy operation.
The user-specific package source root configuration is parsed and expanded
at the start of the invocation into special directories under ~/.cache
and
~/.local/share
.
When a package name foo
is requested, every package source is queried in the
order configured, until one source is found, or it is realised the package
does not exist.
This resolution order is true for every package name lookup, so it applies to
dependency queries, too.
description
(string)Contains a free form textual description for the package which is printed in
the "help" when dotfiles.py
is invoked without any arguments.
dependencies
(list of other package names)The logical names of packages which must be installed before the installation of the current package could begin.
depend on parent
(boolean, default: true
)Whether the package should implicitly depend on the parent (e.g. for
tools.system
, parent is tools
), assuming the parent is a valid package.
support
(boolean, default: false
)Support packages are packages which have all the necessary directives to perform an installation, but are NOT meant to do persistent changes to the user's environment and files.
Support packages can be depended upon, but may not be directly installed by
the user.
A support package's "installed" status will not be saved.
Support packages may not have uninstall
actions.
Packages with internal
in their name (such as internal.mypkg
) will
automatically be considered as support packages.
if
and if not
)The package descriptor might take the if
and if not
keys, which specifies
a list of strings, each associated with a condition.
(See the table below for the options.)
Conditions for the entire package require satisfaction, without which the package will NOT be visible to the installer.
description: A package that can only be installed if 'sudo'.
if:
- superuser
install:
- action: shell
command: "sudo echo 'Hey root!'"
Action directives may specify a list of conditions, with the $if
and
$if not
meta-arguments.
The conditions in the $if
list (positive conditions) must all be
satisfied, and none of the conditions in the $if not
(negative conditions)
list may be satisfied for the action to execute.
If the conditions aren't as described for the action, the action will be
skipped, but the rest of the actions will be executed.
$if
and $if not
can both be specified on the same action.
If neither is specified, the action is not conditional, and will always
execute.
- action: print
$if:
- superuser
text: "I have sudo!"
- action: print
$if not:
- superuser
text: "I do not have sudo!"
- action: print
$if:
- superuser
$if not:
- superuser
text: "Impossible to execute... hopefully."
The contents of the package-level and the action-level condition lists are the same:
Condition | Semantics |
---|---|
os-darwin |
Executes the action only if the package is being installed for Apple macOS systems. |
superuser |
Turns one action into a conditional action which is only executed if the user presents sudo rights. |
The installation of a package consists of two separate phases.
Before the installation of the first package, a temporary $SESSION_DIR
is
created where temporary resources may be shared between packages.
This directory is deleted at the end of execution of all installs.
The usage of this feature is discouraged unless absolutely necessary.
Action directives are laid out in the package descriptor YAML file as
shown below.
Each phase has a list of key-value tuples, which will be executed in the
order they are added.
Each tuple must have an action
argument which defines the type/kind of
the action to run.
The rest of the arguments to specify are specific to the action
type
identifier.
All the arguments that are not meta-arguments (starting with $
) are
specific to the action
type identifier.
prepare:
- action: shell
command: echo "True"
prepare
(optional)First, the package's configuration and external dependencies are obtained. This is called the preparation phase.
At the beginning of this phase, the executing environment switches into a
temporary directory.
In most directives, $TEMPORARY_DIR
can be used to refer to this directory
when specifying a path.
Action | Arguments | Semantics | Failure condition |
---|---|---|---|
copy resource |
path (string) |
Copy the file or directory from the package's resources (where package.yaml is) to the temporary directory |
path is invalid or OS-level permission error occurs |
git clone |
repository (URL string) |
Obtain a clone of the repository by calling git
|
git clone process fails |
print |
text (string) |
Emit the message text to the user on the standard output. |
|
shell |
command (string) |
Execute command in a shell |
Non-zero return |
shell all |
commands (list of strings) |
Execute every command in order | At least one command returns non-zero |
shell any |
commands (list of strings) |
Execute the commands in order until one succeeds | None of the commands returns zero |
install
The installation starts from the packages/foo/bar/
directory (for package
foo.bar
).
This phase is the main phase where changes to the user's files should be
done.
In most directives, $TEMPORARY_DIR
can be used to refer to the prepare
phase's directory when specifying a path.
(This may only be done if there was a prepare
phase for the package!)
$PACKAGE_DIR
refers to the directory where the package's metadata file
(package.yaml
) and additional resources are.
Action | Arguments | Semantics | Failure condition |
---|---|---|---|
copy |
file (string), to (string) |
Copies file to the to path |
OS-level error |
copy |
files (list of string), to (string), from (string, default: $PACKAGE_DIR ), prefix (string, default: empty) |
As if copy was done for all element of files understood relative to from ; to must be the destination directory, optionally prepending prefix to each destination filename |
OS-level error, to isn't an existing directory |
copy tree |
dir (string), to (string) |
Copies the contents of dir to the to directory, to is created by this call |
OS-level error, to is an existing directory |
make dirs |
dirs (list of strings) |
Creates the directories specified, and their parents if they don't exist | OS-level error happens at creating a directory |
remove |
file (string), ignore missing (boolean, default: true ) |
Same as remove for Uninstall, but saves the original in the backup for later restoration |
see failure conditions for remove in Uninstall |
remove |
files (list of string), ignore missing (boolean, default: true ) |
Same as remove for Uninstall, but saves the original in the backup for later restoration |
see failure conditions for remove in Uninstall |
remove |
where (string), file or files , ignore missing as above |
Same as remove for Uninstall, but saves the original in the backup for later restoration |
see failure conditions for remove in Uninstall |
replace |
at (string), with file (string), with files (list of strings), from (string, default: empty), prefix (string, default: empty) |
Does the same as copy but also prepares restoring (at uninstall) the original target files if they existed |
see failure conditions for copy |
print |
text (string) |
Emit the message text to the user on the standard output. |
|
shell |
command (string) |
Execute command in a shell |
Non-zero return |
shell all |
commands (list of strings) |
Execute every command in order | At least one command returns non-zero |
shell any |
commands (list of strings) |
Execute the commands in order until one succeeds | None of the commands returns zero |
symlink |
file (string), to (string), relative (boolean, default: false ) |
Same as copy , but creates a symbolic link instead; if relative is true , the symbolic link will be created relatively from its target location. |
see failure conditions for copy |
symlink |
files (list of strings), to (string), from (string, default: $PACKAGE_DIR ), prefix (string, default: empty), relative (boolean, default: false ) |
Same as copy , but create symbolic links instead; if relative is true , the symbolic links will be created relatively from their target location. |
see failure conditions for copy |
uninstall
Uninstall starts from the directory that corresponds to $PACKAGE_DIR
, but
contains the files that were present in this directory at installation, not
what are currently present on the system.
This means that the state of package.yaml
and the resource files are kept
intact even if the packages/
directory on the system is updated.
In uninstall
mode, $PACKAGE_DIR
refers to this archived directory.
⚠️ Note! The "original" $PACKAGE_DIR
is not accessible during
uninstall
.
Action | Arguments | Semantics | Failure condition |
---|---|---|---|
remove |
file (string), ignore missing (boolean, default: true ) |
Removes the specified file if it exists, raising an error if ignore missing is false ; file must be an absolute path |
OS-level error happens at removal |
remove |
files (list of strings, ignore missing (boolean, default: true ) |
Removes all files specified, all files must be specified as an absolute path | OS-level error happens at removal |
remove |
where (string), file or files , ignore missing as above |
As above, but file or files may be a relative path, understood relative to where , where must be an existing directory's absolute path |
OS-level error, where isn't an existing directory |
remove dirs |
dirs (list of strings) |
Removes the specified directory, if it is empty | OS-level error happens at removing a directory |
remove tree |
dir (string) |
Removes the tree (all subdirectories and files) under dir
|
OS-level error, dir isn't an existing directory |
restore |
file (string) |
Restores the version of the file (which must be an absolute path) that was present when the package was installed, if such version exists |
OS-level error |
restore |
files (list of strings) |
Calls restore for each file in files
|
OS-level error |
print |
text (string) |
Emit the message text to the user on the standard output. |
|
shell |
command (string) |
Execute command in a shell |
Non-zero return |
shell all |
commands (list of strings) |
Execute every command in order | At least one command returns non-zero |
shell any |
commands (list of strings) |
Execute the commands in order until one succeeds | None of the commands returns zero |
uninstall
actions for install
directivesCertain install
directives can automatically be mapped to uninstall
actions.
At the uninstall of a package, the corresponding actions are executed in
reverse order (compared to the order of install
directives).
If automatic uninstall actions were generated for a package's install, they
are executed after the manually written uninstall
directives are
executed.
install action |
uninstall action |
Comment |
---|---|---|
copy |
remove |
file and files are translated in a reasonable manner, where is not filled automatically, paths containing environment variables are kept as such for uninstall! |
copy tree(dir, to) |
remove tree(to) |
|
make dirs(dirs) |
remove dirs(dirs) |
|
remove |
restore |
file and files are translated in a reasonable manner, where is not filled automatically, paths containing environment variables are kept as such for uninstall! |
replace |
restore |
with file and with files are translated in a reasonable manner, at is ignored as paths are translated into absolute paths but retain environment variables |
symlink |
remove |
see details for copy |
Automatic transformation of the executed actions might be beneficial in some
circumstances.
All transformers are enabled with the --X
flag, followed by the
transformer's name (with dashes).
To forbid a transformer from running for an action, add its name set to false
under the $transform
meta-argument.
install:
- action: copy
file: foo
to: bar
$transform:
copies as symlinks: false
copies-as-symlinks
Instead of executing copy
-like directives during install
normally, use
the symlink
action to set up symbolic links.
The links will target the original files in the package source.
This allows easy versioning of configuration file changes if the source is a
versioned repository, as the original package source tree will have the files
modified.