Manage your dotfiles and their dependencies automagically
$ brew install pablopunk/brew/dot
$ cd /path/to/dotfiles
$ tree
profiles/
├── work.lua
├── personal.lua
├── linux-server.lua
modules/
├── neovim/
│ ├── init.lua
│ └── config/
├── zsh/
│ ├── init.lua
│ └── zshrc
└── apps/
├── work/
│ └── init.lua
└── personal/
└── init.lua
$ dot # Link all dotfiles and install dependencies
$ dot neovim # Only process the 'neovim' module
$ dot work # Only process the 'work' profile
Each module under the modules/
folder needs to have at least an init.lua
. If not, it will be ignored.
Example for neovim:
-- modules/neovim/init.lua
return {
brew = {
{ name = "neovim", options = "--HEAD" },
"ripgrep"
},
config = {
source = "./config", -- Our config directory within the module
output = "~/.config/nvim", -- Where the config will be linked to
}
}
The config will be linked to the home folder with a soft link. In this case:
~/.config/nvim → ~/dotfiles/modules/neovim/config
You can also specify multiple configurations for a single module:
-- modules/multi-config/init.lua
return {
brew = { "cursor" },
config = {
{
source = "./config/settings.json",
output = "~/Library/Application Support/Cursor/User/settings.json",
},
{
source = "./config/keybindings.json",
output = "~/Library/Application Support/Cursor/User/keybindings.json",
}
}
}
This will create two symlinks:
~/Library/Application Support/Cursor/User/settings.json → ~/dotfiles/modules/multi-config/config/settings.json
~/Library/Application Support/Cursor/User/keybindings.json → ~/dotfiles/modules/multi-config/config/keybindings.json
As you can see, you can declare dependencies as Homebrew packages, which makes it possible to also use dot
to install GUI apps (Homebrew casks). You can create a module without any config to use it as an installer for your apps:
-- modules/apps/init.lua
return {
brew = { "whatsapp", "spotify", "slack", "vscode" }
}
In the example above, let's say we want to separate our apps into "work" and "personal". We could either create 2 modules on the root folder or create a nested folder for each:
-- modules/apps/work/init.lua
return {
brew = { "slack", "vscode" }
}
-- modules/apps/personal/init.lua
return {
brew = { "whatsapp", "spotify" }
}
If you have several machines, you might not want to install all tools on every computer. That's why dot
allows profiles.
Let's create a new "personal" profile:
-- profiles/personal.lua
return {
modules = {
"*",
"!apps/work",
}
}
In this example, running dot personal
will:
*
: Install everything undermodules/
, including nested directories.!apps/work
: Exclude theapps/work
module and its submodules.
You can use the following patterns in your profile:
"*"
: Include all modules recursively."!module_name"
: Exclude a specific module and its submodules."module_name"
: Include a specific module.
For example, a work profile might look like this:
-- profiles/work.lua
return {
modules = {
"apps/work",
"slack",
"neovim",
"zsh"
}
}
Note
If your profile is named just like a module (e.g., profiles/neovim
and modules/neovim
), running dot neovim
will default to the profile.
By default, dot
won't touch your existing dotfiles if the destination already exists. If you still want to replace them, you can use the -f
flag:
$ dot -f neovim
Note
It won't remove the existing config but will move it to a new path: <path-to-config>.before-dot
.
If you want to remove the symlinks created by dot
for a specific module but keep your configuration, you can use the --unlink
option:
$ dot --unlink neovim
This command will:
- Remove the symlink at the destination specified in
config.output
. - Copy the config source from
config.source
to the output location.
This is useful if you want to maintain your configuration files without dot
managing them anymore.
To completely remove a module, including uninstalling its dependencies and removing its configuration, use the --purge
option:
$ dot --purge neovim
This command will:
- Uninstall the Homebrew dependencies listed in the module's
init.lua
. - Remove the symlink or config file/directory specified in
config.output
. - Run any
post_purge
hooks if defined in the module.
Warning
--purge
will uninstall packages from your system and remove configuration files. Use with caution.
You can define post_install
and post_purge
hooks in your module's init.lua
to run arbitrary commands after the module has been installed or purged.
return {
brew = { "gh" },
post_install = "gh auth login"
}
You can also define multi-line hooks:
return {
brew = { "gh" },
post_install = [[
gh auth status | grep 'Logged in to github.com account' > /dev/null || gh auth login --web -h github.com
gh extension list | grep gh-copilot > /dev/null || gh extension install github/gh-copilot
]],
}
-
Install Modules: Install dependencies and link configurations.
$ dot # Install all modules $ dot neovim # Install only the 'neovim' module $ dot work # Install only the 'work' profile
-
Force Mode: Replace existing configurations, backing them up to
<config>.before-dot
.$ dot -f # Force install all modules $ dot -f neovim # Force install the 'neovim' module
-
Unlink Configs: Remove symlinks but keep the config files in their destination.
$ dot --unlink neovim
-
Purge Modules: Uninstall dependencies and remove configurations.
$ dot --purge neovim
It now supports downloading files using wget
. This can be useful for fetching binaries or other resources that are not available through Homebrew. You can specify a wget
configuration in your module's init.lua
file.
Example:
-- modules/apps/init.lua
return {
wget = {
{
url = "https://app1piece.com/1Piece-4.2.1.zip",
output = "/Applications/1Piece.app",
zip = true,
},
{
url = "https://fakedomain.com/fake.app",
output = "/Applications/Fake.app",
},
},
}
This will:
- Download the file from the specified URL.
- Unzip it if
zip = true
. - Install it to the specified output path.
Just use it like any other module:
$ dot apps
Note
When using zip = true
, make sure the output file name matches the unzipped file name. In the example above, the output is /Applications/1Piece.app
because the zip file contains a file named 1Piece.app
.
- pablopunk/dotfiles: my own dotfiles.
-
dot
will install dependencies and link files. - Support Homebrew dependencies.
-
dot -f
will remove the existing configs if they exist (moves config to*.before-dot
). - Allow post-install hooks in bash.
- Allow installing only one module with
dot neovim
. - Allow multiple setups in one repo. Similar to "hosts" in Nix,
dot work
readsprofiles/work.lua
which includes whatever it wants frommodules/
. - Package and distribute
dot
through Homebrew. - Add
--unlink
option to remove symlinks and copy configs to output. - Add
--purge
option to uninstall dependencies and remove configurations. - Allow array of config. For example I could like two separate folders that are not siblings
- Improve profiles syntax. For example,
{ "*", "apps/work" }
should still be recursive except in "apps/". Or maybe accept negative patterns like{ "!apps/personal" }
-> everything but apps/personal. - Add screenshots to the README.
- Support more ways of adding dependencies (e.g., wget binaries, git clone, apt...).
- wget
- git clone
- apt
- Unlinking dotfiles without copying. An option like
dot --unlink --no-copy
could be added. -
dot --purge-all
to purge all modules at once. - Support Mac defaults, similar to
nix-darwin
. - Support an
os
field. i.eos = { "mac" }
will be ignored on Linux. - After using a profile, like
dot profile1
, it should remember it and all calls todot
should be done with this profile unless another profile is explicitely invoked, likedot profile2
, which will replace it for the next invokations.