Documentation
¶
Overview ¶
Package shell provides the ShellProfileHandler implementation for dodot.
Overview ¶
The ShellProfileHandler manages shell configuration by sourcing shell scripts into your shell environment. It provides a non-invasive way to add aliases, functions, environment variables, and other shell customizations without directly modifying shell configuration files like .bashrc or .zshrc.
When It Runs ¶
- **Deploy Mode**: YES - Runs during `dodot deploy` (RunModeLinking) - **Install Mode**: NO - Does not run during `dodot install` - **Idempotent**: YES - Safe to run multiple times
Standard Configuration ¶
Currently not configured by default. To enable, add to your config:
In your global dodot configuration (~/.config/dodot/config.toml):
[matchers.shell-profiles] trigger = "filename" patterns = ["aliases.sh", "profile.sh", "*.profile.sh"] handler = "shell" priority = 80
Or in a pack-specific .dodot.toml:
[[matchers]] trigger = "directory" path = "shell" handler = "shell"
File Selection Process ¶
1. **Pack Discovery**: dodot finds all subdirectories in $DOTFILES_ROOT 2. **File Walking**: Recursively walks each pack directory 3. **Trigger Matching**: FileNameTrigger matches shell scripts (e.g., aliases.sh) 4. **Handler Invocation**: Matched files are passed to ShellProfileHandler.Process()
Example file structure:
~/dotfiles/
├── base/
│ └── aliases.sh # Common aliases for all systems
├── git/
│ └── git-profile.sh # Git-specific aliases and functions
└── dev/
└── dev.profile.sh # Development environment setup
Execution Strategy ¶
dodot uses a centralized sourcing approach for shell profiles:
**Step 1 - Registration**: - Creates entries in ~/.local/share/dodot/shell/init.sh - Each entry sources a matched shell script with existence check
**Step 2 - Shell Integration**: - User's shell config sources dodot-init.sh (one-time setup) - dodot-init.sh sources all registered scripts on shell startup
**Benefits**: - Non-invasive: Only one line added to user's shell config - Dynamic: Add/remove scripts without editing .bashrc/.zshrc - Organized: All customizations tracked in one place - Debuggable: DODOT_SHELL_SOURCE_FLAG tracks sourced files
Storage Locations ¶
- **Registration file**: ~/.local/share/dodot/shell/init.sh - **Deployed scripts**: Referenced from their original pack locations - **No copying**: Scripts are sourced directly from dotfiles - **No sentinels**: Deploy actions are always idempotent
Environment Variable Tracking ¶
Sourced shell profiles are tracked via the `DODOT_SHELL_PROFILES` environment variable when dodot-init.sh is sourced:
- **Variable**: `DODOT_SHELL_PROFILES` - **Format**: Colon-separated list of relative paths from dotfiles root - **Example**: `base/aliases.sh:git/git-profile.sh:dev/dev.profile.sh` - **Usage**: `echo $DODOT_SHELL_PROFILES | tr ':' '\n'` to list all profiles
This allows you to see which shell scripts are being sourced into your environment.
Shell Compatibility ¶
The generated source commands work with: - **Bash**: Full support - **Zsh**: Full support - **Fish**: Requires bass (bash compatibility layer) - **POSIX sh**: Basic support (no advanced features)
Scripts should use POSIX-compatible syntax when possible for maximum compatibility.
Effects on User Environment ¶
- **Sources scripts**: On every new shell session - **Adds to environment**: Aliases, functions, variables defined in scripts - **Modifies PATH**: If scripts export PATH changes - **Reversible**: Remove entries from init.sh to stop sourcing - **No backups needed**: Original shell configs untouched
Options ¶
Currently, ShellProfileHandler accepts no configuration options. All behavior is controlled by the scripts themselves.
Script Template ¶
Use `dodot new shell-profile <pack>` to create a script from template:
#!/usr/bin/env bash
# Shell profile for <pack>
# Aliases
alias ll='ls -la'
alias gs='git status'
# Functions
mkcd() {
mkdir -p "$1" && cd "$1"
}
# Environment variables
export EDITOR=vim
# Path modifications
export PATH="$HOME/.local/bin:$PATH"
Example End-to-End Flow ¶
User runs: `dodot deploy`
- dodot finds ~/dotfiles/git/aliases.sh
- FileNameTrigger matches "aliases.sh" pattern
- ShellProfileHandler creates ActionTypeShellSource action
- DirectExecutor appends to ~/.local/share/dodot/shell/init.sh: ``` # Source aliases.sh from git [ -f "/home/user/dotfiles/git/aliases.sh" ] && source "/home/user/dotfiles/git/aliases.sh" ```
- On next shell startup, dodot-init.sh sources init.sh
- Git aliases become available in the shell
- User can edit aliases.sh and changes take effect on next shell
Error Handling ¶
Shell sourcing errors are handled gracefully:
- **File not found**: Silently skipped (existence check) - **Syntax errors**: Reported by shell but don't break startup - **Permission errors**: Logged but shell continues - **Circular dependencies**: User's responsibility to avoid
Best Practices ¶
1. **Use descriptive names**: `git-aliases.sh` better than `aliases.sh` 2. **Check dependencies**: Verify commands exist before aliasing 3. **Avoid side effects**: Don't run commands, just define them 4. **Group by function**: One script per tool/purpose 5. **Document complex functions**: Add comments explaining usage 6. **Test portability**: Ensure scripts work in bash and zsh 7. **Prefix functions**: Avoid naming conflicts (e.g., `git_branch()`)
Common Use Cases ¶
- **Aliases**: Shortcuts for common commands - **Functions**: Complex operations wrapped in functions - **Prompts**: Custom PS1/PROMPT configurations - **Completions**: Load tool-specific completions - **Environment**: Set EDITOR, PAGER, etc. - **PATH setup**: Add directories to PATH - **Tool initialization**: Load nvm, rbenv, etc.
Integration with dodot-init.sh ¶
The one-time setup adds to your shell config:
# For bash (~/.bashrc) [ -f "$HOME/.local/share/dodot/init/dodot-init.sh" ] && source "$HOME/.local/share/dodot/init/dodot-init.sh" # For zsh (~/.zshrc) [ -f "$HOME/.local/share/dodot/init/dodot-init.sh" ] && source "$HOME/.local/share/dodot/init/dodot-init.sh" # For fish (~/.config/fish/config.fish) [ -f "$HOME/.local/share/dodot/init/dodot-init.fish" ] && source "$HOME/.local/share/dodot/init/dodot-init.fish"
Comparison with Other Handlers ¶
- **SymlinkHandler**: For static config files - **PathHandler**: Specifically for PATH modifications - **ShellProfileHandler**: For dynamic shell environment setup - **InstallScriptHandler**: For one-time setup tasks
Use ShellProfileHandler when you need to add dynamic behavior to your shell environment, such as aliases, functions, or conditional configuration that varies by machine or environment.
Index ¶
- Constants
- type Handler
- func (h *Handler) CheckStatus(file operations.FileInput, checker operations.StatusChecker) (operations.HandlerStatus, error)
- func (h *Handler) GetMetadata() operations.HandlerMetadata
- func (h *Handler) GetTemplateContent() string
- func (h *Handler) ToOperations(files []operations.FileInput) ([]operations.Operation, error)
Constants ¶
const ShellHandlerName = "shell"
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Handler ¶
type Handler struct {
operations.BaseHandler
}
Handler implements the new simplified handler interface. It transforms shell profile requests into operations without performing any I/O.
func (*Handler) CheckStatus ¶
func (h *Handler) CheckStatus(file operations.FileInput, checker operations.StatusChecker) (operations.HandlerStatus, error)
CheckStatus checks if the shell config file has been linked
func (*Handler) GetMetadata ¶
func (h *Handler) GetMetadata() operations.HandlerMetadata
GetMetadata returns handler metadata.
func (*Handler) GetTemplateContent ¶
GetTemplateContent returns the template content for this handler.
func (*Handler) ToOperations ¶
func (h *Handler) ToOperations(files []operations.FileInput) ([]operations.Operation, error)
ToOperations converts file inputs to shell operations. Shell scripts require only CreateDataLink - shell initialization handles sourcing.