OCD 0.8.0 Usage Guide
A basic guide to using OCD 0.8.0
OCD is an experimental dotfile management tool that allows the user to manage their dotfiles through a collection of deployable repositories called a cluster. Upon deployment, the user can issue Git commands interactively to manage their dotfiles within a given repository apart of their cluster.
The Concept of a Cluster
OCD operates on the concept of a cluster. A cluster is a collection of
repositories that can be deployed together. The cluster is made up of the
cluster definition and the repository store. The cluster definition
houses configuration files that defines each entry of the repository store.
The repository store houses the Git repositories defined as entries of the
cluster. The cluster definition is always expected to be at
XDG_CONFIG_HOME/ocd
, and the repository store is expected to be at
$XDG_DATA_HOME/ocd
.
A given cluster entry must be written in the TOML data exchange format version 1.0. The user is responsible for defining any cluster entry they want OCD to manage. A cluster entry can either be a node or a root.
Node Entry Layout
A node entry is a basic repository entry apart of the cluster. It must be
defined in the nodes directory at the top-level of the cluster definition,
and must be named after the node it defines in the repository store. For
example, $XDG_CONFIG_HOME/ocd/nodes/vim.toml
corresponds to
$XDG_DATA_HOME/ocd/vim
in the repository store. Any node entry defined outside
of the nodes directory will be silently ignored. A node entry must contain
the following two key-value pairs in the settings table: a deployment
and url key-value pair.
The deployment key-value pair specifies the deployment method of the node. There exists two deployment methods: normal and bare-alias. Normal deployment simly means that OCD will make sure that the node has been cloned as a normal repository. Bare-alias deployment means that OCd will clone the node as a bare repository, and will use an external directory as an alias for a worktree. Thus, OCD can deploy the contents of the node to that working directory alias. This gives the user the ability to treat a target directory as a Git repository, without initializing it as one. The deployment key-value pair can be defined to accept a string value, or an inline-table value. Here is the expected layouts:
Normal deployment layout:
1
2
[settings]
deployment = "normal"
Bare-alias deployment layout (default to $HOME):
1
2
[settings]
deployment = "bare-alias"
Bare-alias deployment layout (custom working directory alias):
1
2
[settings]
deployment = { kind = "bare_alias", work_dir_alias = "$HOME/somewhere" }
The third deployment layout option show above should generally be used for bare-alias deployment only. The
kind
key-value pair can accept “normal” deployment, but any value entered into thework_dir_alias
key-value pair will be silently ignored.
The
work_dir_alias
key-value pair will be shell expanded if it contains environment variables.
Continuing, the url key-value pair simply specifies where to clone the node entry from. It can accept any string value that represents a valid URL that Git can interpret. Thus, here is an example of a minimal node entry configuration:
1
2
3
[settings]
deployment = "bare_alias"
url = "https://github.com/user/vim.git"
Optionally, a node entry can contain a list of files to exclude from deployment through the excluded key-value pair, or a list of other nodes to use as dependencies via the dependencies key-value pair. The excluded key-value pair accepts a list of strings representing valid gitignore-style patterns. The dependencies key-value pair accepts a list of strings containing the names of the nodes to deploy. Here is an example of a fully decked out node entry:
1
2
3
4
5
[settings]
deployment = { kind = "bare_alias", work_dir_alias = "$HOME/foobar" }
url = "https://github.com/user/foobar.git"
excluded = ["README*", "LICENSE*", ".github/"]
dependencies = ["foo", "bar", "baz"]
Node entries must abide by the following preconditions:
- Node dependencies must be defined in cluster definition.
- Node dependencies must be acyclic.
Root Entry Layout
The root is a specialized bare-alias entry of a cluster. This special entry
contains the cluster definition itself for deployment to new machines. There can
only be one root, and it must be defined at $XDG_CONFIG_HOME/ocd/root.toml
at
all times. OCD will error out if root is not defined.
The root entry configuration file must contain a settings table similar to a node entry configuration file. The work_dir_alias key-value pair must be defined. This key-value pair can accept only two string values: config_dir, or home_dir. The config_dir option will make OCD deploy the root repository to $XDG_CONFIG_HOME/ocd, while the home_dir option will make OCD deploy root directly to the user’s home directory. These are the only two locations that OCD accepts. Optionally, the user can specify a list of files to exclude from deployment, the same as the excluded key-value pair for node entries. Here is an example configuration for root:
1
2
3
[settings]
work_dir_alias = "config_dir"
excluded = ["README*", "LICENSE*", ".github/"]
OCD will store the
root.toml
file in two locations in the root repository itself depending of the working directory alias target:
- If using
config_dir
, thenroot.toml
will be at the top-level of the root repository.- If using
home_dir
, thenroot.toml will be at
.config/ocd/root.toml` in the root repository.OCD will error out if the user does not follow this convention when setting up or modifying their root repository.
Command Hooks
The user can define custom command hooks to execute as apart of their cluster. Command hooks utilize two components: an entry in the command hook configuration file, and a hook script to execute with. A command hook entry must be defined in $XDG_CONFIG_HOME/ocd/hooks.toml. Hook scripts must be placed into the $XDG_CONFIG_HOME/ocd/hooks directory.
A command hook entry can define a hook script to execute before (pre) or after (post) a given command. Some commands may even allow a command hook to only execute for a target repository. Each entry must be defined under the hooks table. Here is the basic layout:
1
2
3
4
5
6
[hooks]
<ocd-command> = [
{ pre = "hook.sh", work_dir = "$HOME" },
{ post = "hook.sh" },
{ pre = "hook.sh", post = "hook.sh", work_dir = "$HOME", target = "vim" },
]
The <ocd-command>
field represents the name of a valid OCD command to tie each
hook entry to. Each key-value pair for the hook entries shown above are all
optional. Thus, while not recommended, an empty hook entry is considered valid.
The work_dir key-value pair is always shell expanded, if it not defined,
then OCD will use the current working directory instead. When selecting a hook
script for either of the pre or post key-value pairs, the name of the
hook script should be used only. Finally, the target key-value pair can be
used to tie a given hook entry to a target entry in the cluster. If no
target key-value pair is provided, then the hook entry will execute
regardless of cluster entry a given OCD command is executing on.
OCD only checks for hook scripts relative to $XDG_CONFIG_HOME/ocd/hooks. It will not look anywhere else. Thus, using absolute paths relative to the user home directory, or anywhere else not the hooks directory, will cause OCD to fail in locating a hook script for a given hook entry.
Finally, all OCD commands have access to command hooks. The only exceptions are the Git ocmmand shortcut that allows the user to issue Git commands on target entries of their cluster, and the help command. However, the special targeting feature offered by the target key-valu epair is not supported by all OCD commands. As a general rul of thumb, any OCD command that does not allow the user to target specific entries in their cluster will not support the command hook targeting feature.
Multi-Targeting
Most commands allow for multi-targeting of entries in the cluster. The user can supply a comma separated list of names. The user can also use glob matching patterns to enhance targeting. For example:
1
2
3
ocd deploy "vim,*sh,[a-z]oo"
ocd undeploy "vim,*sh,[a-z]oo"
ocd rm "vim,*sh,[a-z]oo"
Keep in mind, in order to target the root of a cluster, then “root” must be given as a target in full. Glob matching patterns only apply to node entries. Thus, if the user wants to pull changes from all entries in the cluster, then the would need to do the following:
1
ocd "root,*" pull origin main
Be sure to quote glob patterns, otherwise the shell will expand those patterns instead of OCD.
Examples
Build New Modular Cluster
Assume that the following root configuration flie was defined beforehand:
$XDG_CONFIG_HOME/ocd/root.toml:
1
2
3
[settings]
work_dir_alias = "config_dir"
excluded = ["README*", "LICENSE*"]
Now initialize, and create initial commit:
1
2
3
4
5
6
ocd init root
cd ~/.config/ocd
ocd root remote add origin git@github.com:user/root.git
ocd root add root.toml
ocd root commit -m "Initial commit"
ocd root push -u origin main
Now that root is setup, lets add a new node entry. Assume we have a Bash configuration through .bashrc and .bash_profile. However, the PS1 of this configuraiton relies on a special shell script named polyglot.sh stored at https://github.com/agkozak/polyglot.git. To ensure our Bash configuration is functional, we need to create two node entries such that one will act as the dependency of the other:
$XDG_CONFIG_HOME/ocd/nodes/polyglot_ps1.toml:
1
2
3
4
[settings]
deployment = { kind = "bare_alias", work_dir_alias = "$HOME/.local/share" }
url = "https://github.com/agkozak/polyglot.git"
excluded = [".git*", "img/", "vimrc.local", "README*", "LICENSE*", "*.zsh"]
$XDG_CONFIG_HOME/ocd/nodes/bash.toml:
1
2
3
4
[settings]
deployment = "bare_alias
url = "https://github.com/user/bash.git"
dependencies = ["polyglot_ps1"]
Finally, we initalize, commit, and deploy the bash node:
1
2
3
4
5
6
7
ocd init bash
cd ~
ocd bash remote add origin https://github.com/user/bash.git
ocd bash add .bashrc .bash_profile
ocd bash commit -m "Initial commit"
ocd bash push -u origin main
ocd deploy bash
We do not need to initialize the polyglot_ps1 node, because OCD will automatically clone its url field. We also do not need to deploy it directly, because we stated that polyglot_ps1 is a dependency of the bash node. Finally, the polyglot_ps1 node will also ensure that polyglot.sh gets deployed to $HOME/.local/share since all other files will be excluded from deployment.
Finally, we want to transfer this new cluster to a new machine. Firstly, make sure that all node entry configurations have been committed to root:
1
2
3
4
cd ~/.config/ocd
ocd root add nodes/*
ocd commit -m "Update node entries in cluster"
ocd push origin main
Now head over to the target machine, and use the clone command after installing OCD:
1
ocd clone git@github.com:user/root.git
OCD will clone and deploy the root repository, and clone all node entries in one shot. You can then use the deploy command to deploy all nodes that were created like so:
1
ocd deploy "*"
Build New Monolithic Cluster
Sometimes the user may want to keep track of a small set of dotfiles that they just want to dump into a single place for convienence. Or they just want to have a minimal deployable dotfile configuration for a server environment. Whatever the reason, OCD allows for the root repository to be used as the main area to house dotfiles besides housing the cluster definition.
Firstly, we need to define a root configuration that uses our home directory as the working directory alias:
$XDG_CONFIG_HOME/ocd/root.toml: [settings] work_dir_alias = “home_dir”
Now initialize root, and we can begin dumping dotfile configurations relative to our home directory:
1
2
3
4
5
6
cd ~
ocd init root
ocd root remote add origin git@github.com:user/root.git
ocd root add .config/ocd/* .vimrc .bash_profile .bashrc
ocd root commit -m "Initial commit"
ocd push -u origin main
Finally, we can use the clone command the same way we would for a modular cluster in order to transfer this cluster to a new machine. We can add node entries to this cluster if we wanted to, but we just need to keep in mind that root’s working directory alias is now pointing to our home directory instead of OCD’s configuration directory.