dloom

A lightweight, flexible dotfile manager and system bootstrapper for macOS and Linux.

Build Status GitHub Release Ubuntu Snap Ubuntu Snap License

Overview

dloom (pronounced dee-loom) is a CLI (command-line interface) tool that links and unlinks configuration files (or dotfiles) in a development machine. It manages symlinks between a dotfiles repository (or any source directory) and the machine’s target directory (home directory by default; overridable). The tool is inspired from GNU Stow and other dotfile managers, but differs in its approach by creating symlinks for individual files instead of directories. This enables other applications to add files to the same parent directories without them needing to be tracked in the dotfiles repository.

Features

Installation

Pre-built Binaries

dloom is a single cross-platform binary and can be installed on macOS and Linux. You can download the latest release from the GitHub releases page. Simply download the dloom binary, and place it in your PATH.

From Source

Requirements:

# Install from source
go install github.com/dloomorg/dloom

# Or clone and build
git clone https://github.com/dloomorg/dloom.git
cd dloom
go build -o bin/dloom

Quick Start

dloom has two main commands: link and unlink. The link command creates symlinks for your dotfiles, while the unlink command removes them.

Linking Dotfiles

To link your dotfiles, run:

# Link all dotfiles from your vim package
dloom link vim

# Link multiple packages
dloom link vim tmux bash

# Link with verbose output
dloom -v link vim

# Preview changes without making them
dloom -d link vim

Consider this example dotfiles repository:

~/dotfiles/
├── vim/
    ├── .vimrc
    └── .config/
        └── plugins.vim

When you run dloom link vim, it will create:

~/                                                          ~/dotfiles/
  ├── .vimrc ------------------------------------------>      ├── vim/.vimrc
  └── .config/ (regular directory; created if not exists)     └── .config/
      └── plugins.vim --------------------------------->          └── plugins.vim

Notice that:

Different commands and their effects:

# Link vim package to home directory
dloom link vim

# Link vim package to a different target directory
dloom -t ~/.config/nvim link vim
# Creates: ~/.config/nvim/.vimrc → ~/dotfiles/vim/.vimrc
#          ~/.config/nvim/.config/plugins.vim → ~/dotfiles/vim/.config/plugins.vim

# Link from a different source directory
dloom -s /path/to/dotfiles link vim
# Uses: /path/to/dotfiles/vim/ as the source

# Dry run to preview changes
dloom -d link vim
# Output:
# Would create a regular directory: /home/user/.config
# Would link: /home/user/.vimrc → /home/user/dotfiles/vim/.vimrc
# Would link: /home/user/.config/plugins.vim → /home/user/dotfiles/vim/.config/plugins.vim

Unlinking Dotfiles

To remove the symlinks created by dloom, use the unlink command:

# Unlink all dotfiles from your vim package
dloom unlink vim

Unlink will only remove links if they were created by dloom, i.e - if the links are pointing to files in the source (usually the dotfiles) directory. Any extra files in the target directory will remain untouched. If dloom finds any backups for files that were unlinked, it will restore them. Finally, if the target directory becomes empty after unlinking (and if no backups were found), the directory will be removed.

Backup System

dloom automatically backs up existing files before replacing them. The backup files are stored in the backup_dir specified in the configuration file (default: ~/.dloom/backups). If a file already exists at the target location, it will be backed up before creating the symlink. During the unlinking process, if a backup exists, it will be restored.

Dry Run

To preview what would happen without making any changes, use the -d or --dry-run option:

dloom -d link vim

Sample output:

➜  dotfiles git:(main) ✗ dloom -d link dloom zsh alacritty kitty ghostty eza sway waybar
[DRY RUN]: Would link: /home/user_name/.config/dloom/config.yaml -> /home/user_name/dotfiles/dloom/config.yaml
[DRY RUN]: Would link: /home/user_name/.config/zsh/.zshrc-common.zsh -> /home/user_name/dotfiles/zsh/.config/zsh/.zshrc-common.zsh
[DRY RUN]: Would link: /home/user_name/.config/zsh/.zshrc-git.zsh -> /home/user_name/dotfiles/zsh/.config/zsh/.zshrc-git.zsh
[DRY RUN]: Would link: /home/user_name/.config/zsh/.zshrc-nflx.zsh -> /home/user_name/dotfiles/zsh/.config/zsh/.zshrc-nflx.zsh
[DRY RUN]: Would link: /home/user_name/.zshrc -> /home/user_name/dotfiles/zsh/.config/zsh/.zshrc-ubuntu.zsh
[DRY RUN]: Would link: /home/user_name/.config/alacritty/alacritty.toml -> /home/user_name/dotfiles/alacritty/.config/alacritty/alacritty.toml
[DRY RUN]: Would link: /home/user_name/.config/alacritty/catppuccin-mocha.toml -> /home/user_name/dotfiles/alacritty/.config/alacritty/catppuccin-mocha.toml
[DRY RUN]: Would link: /home/user_name/.config/kitty/current-theme.conf -> /home/user_name/dotfiles/kitty/.config/kitty/current-theme.conf
[DRY RUN]: Would link: /home/user_name/.config/kitty/kitty.conf -> /home/user_name/dotfiles/kitty/.config/kitty/kitty.conf
[DRY RUN]: Would link: /home/user_name/.config/ghostty/config -> /home/user_name/dotfiles/ghostty/.config/ghostty/config
[DRY RUN]: Would link: /home/user_name/.config/ghostty/themes/tilix -> /home/user_name/dotfiles/ghostty/.config/ghostty/themes/tilix
[DRY RUN]: Would link: /home/user_name/.config/eza/theme.yml -> /home/user_name/dotfiles/eza/.config/eza/theme.yml
[DRY RUN]: Would link: /home/user_name/.config/sway/config -> /home/user_name/dotfiles/sway/.config/sway/config
[DRY RUN]: Would link: /home/user_name/.config/waybar/configc.json -> /home/user_name/dotfiles/waybar/.config/waybar/configc_sway.json
[DRY RUN]: Would link: /home/user_name/.config/waybar/mocha.css -> /home/user_name/dotfiles/waybar/.config/waybar/mocha.css
[DRY RUN]: Would link: /home/user_name/.config/waybar/start_waybar.sh -> /home/user_name/dotfiles/waybar/.config/waybar/start_waybar_sway.sh
[DRY RUN]: Would link: /home/user_name/.config/waybar/style.css -> /home/user_name/dotfiles/waybar/.config/waybar/style.css

This will show what files would be linked or unlinked without actually performing the operation.

Configuration (Optional)

dloom can be customized using a configuration file to override default settings. This allows users to specify different source and target directories, enable verbose output, force overwriting existing files, and set up conditional linking based on various criteria. Some example use cases include:

Configuration is done via a YAML file, which allows hierarchical overrides from global, package-specific to individual file-specific settings. By default, dloom looks for a config.yaml file in the following directories in order of precedence:

  1. ./dloom/config.yaml (in current working directory)
  2. ~/.config/dloom/config.yaml (in user config directory)

You can also specify a custom config file location with the -c path/to/config.yaml option. For easiest configuration, create a dloom/config.yaml file in the root of your dotfiles repository. You can also use dloom itself to link to this file in your repository from ~/.config/dloom/config.yaml so that you can run dloom from anywhere.

Basic Configuration

# Global settings
source_dir: "~/dotfiles"        # Where your dotfiles are stored; default is current directory
target_dir: "~"                 # Where to create symlinks; default is home directory
backup_dir: "~/.dloom/backups"  # Where to back up existing files; default is ~/.dloom/backups
verbose: true                   # Enable detailed output; default is false
force: false                    # Don't overwrite without asking; default is false
dry_run: false                  # Actually make changes; default is false

# Package-specific settings
link_overrides:
  # The package name; this is just a name to group some related files together; 
  # This does not need to be an executable in the system; 
  # dloom expects a directory with the same name in the source_dir, under which the files to be linked will be found
  vim:
    target_dir: "~/.config/nvim"  # Override target for all files under the vim package
    conditions: # Conditions for linking; multiple conditions can be specified and are AND-ed together
      os: # Operating system; multiple OS can be specified; multiple conditions are OR-ed together
        - "linux"
        - "darwin"  # Only link on Linux or macOS

Advanced Configuration

link_overrides:
  tmux:
    conditions:
      executable:
        - "tmux"  # Only link if tmux is installed
    
    # File-specific configuration overrides (optional)
    # This overrides package settings and only needed if defaults are not sufficient
    # File name matching attempts to match the exact file name first 
    # before trying regex matching regardless of the declaration order of the overrides
    file_overrides:
      # File with regex pattern matching
      "regex:^tmux.*\.local$":
        conditions: # Multiple conditions specified; will be linked only if all conditions are met
          os:
            - "darwin"  # Only link on macOS
          user:
            - "user_name"  # Only link for user 'user_name'
      
      # Version-specific configurations
      "tmux.new.conf": # File name; must match the exact name in the source directory
        target_name: "tmux.conf"  # Creates the link with a different name
        conditions:
          executable_version:
            "tmux": ">=3.0"  # Only link for tmux 3.0+

Full Configuration

For a more complete example, check the examples directory in the repository. It contains various configurations for different setups.

Usage

Linking Dotfiles

# Basic linking
dloom link <package>...

# Link with options
dloom -v -f link <package>...  # Verbose and force overwrite

# Use -d flag for dry run (preview only)
dloom -d link link tmux vim

Unlinking Dotfiles

# Remove symlinks
dloom unlink <package>...

# Unlink with options
dloom -d unlink <package>...  # Dry run (preview only)

Command-line Options

Option Description
-c, --config Path to config file
-f, --force Force overwrite existing files
-v, --verbose Enable verbose output
-d, -n, --dry-run Show what would happen without making changes
-s, --source, --src Source directory
-t, --target, --dest Target directory

Conditional Linking

dloom supports conditional linking based on:

When multiple conditions are specified, they are AND-ed together. For example, if you want to link a file only if the OS is Linux and the executable git is installed, you can specify both conditions in the configuration file. For a given condition, if multiple values are specified, they are OR-ed together. For example, if you want to link a file only if the OS is either Linux or macOS, you can specify both values in the configuration file.

Project Structure

dloom/
├── dloom.go            # Command-line interface entry point
├── internal/           # Internal implementation
│   ├── config.go       # Configuration handling
│   ├── link.go         # Link implementation
│   ├── unlink.go       # Unlink implementation
│   └── setup.go        # System setup implementation
└── examples/           # Sample configurations

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments


dloom - Weave your development environment.