Documentation
¶
Overview ¶
Package install provides the InstallScriptPowerUp implementation for dodot.
Overview ¶
The InstallScriptPowerUp executes shell scripts for one-time setup tasks that should only run once per pack. This is ideal for installing dependencies, downloading resources, creating directories, or performing initial configuration that doesn't need to be repeated.
When It Runs ¶
- **Deploy Mode**: NO - Does not run during `dodot deploy` - **Install Mode**: YES - Runs during `dodot install` (RunModeOnce) - **Idempotent**: YES - Uses checksums to track execution
Standard Configuration ¶
In your global dodot configuration (~/.config/dodot/config.toml):
[matchers.install-scripts] trigger = "filename" patterns = ["install.sh"] powerup = "install_script" priority = 90
Or in a pack-specific .dodot.toml:
[[matchers]] trigger = "filename" patterns = ["setup.sh", "init.sh"] powerup = "install_script"
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 files (typically "install.sh") 4. **PowerUp Invocation**: Matched files are passed to InstallScriptPowerUp.Process()
Example file structure:
~/dotfiles/
├── vim/
│ └── install.sh # Installs vim plugins, creates directories
├── node/
│ └── install.sh # Installs nvm, sets up global packages
└── fonts/
└── install.sh # Downloads and installs custom fonts
Execution Strategy ¶
dodot uses a checksum-based sentinel system to ensure scripts run only once:
1. **First Run**: Script executes, SHA256 checksum is calculated and stored 2. **Subsequent Runs**: Current checksum compared with stored value 3. **Script Modified**: If checksum differs, script runs again 4. **Force Mode**: `--force` flag bypasses checks and re-runs all scripts
Execution process:
- Copy script to ~/.local/share/dodot/install/<pack>/install.sh
- Make script executable (chmod +x)
- Execute with environment variables set
- Store checksum in sentinel file on success
Storage Locations ¶
- **Copied scripts**: ~/.local/share/dodot/install/<pack_name>/ - **Sentinel files**: ~/.local/share/dodot/install/sentinels/<pack_name> - **No cache**: Scripts are always copied fresh from source - **Logs**: Script output captured in execution logs
Environment Variable Tracking ¶
Completed install scripts are tracked via the `DODOT_INSTALL_SCRIPTS` environment variable when dodot-init.sh is sourced:
- **Variable**: `DODOT_INSTALL_SCRIPTS` - **Format**: Colon-separated list of pack names with completed install scripts - **Example**: `vim:node:python:tools` - **Usage**: `echo $DODOT_INSTALL_SCRIPTS | tr ':' '\n'` to list completed packs
This helps track which packs have had their install scripts run successfully.
Environment Variables ¶
Scripts execute with these environment variables available:
- **DOTFILES_ROOT**: Path to your dotfiles directory - **DODOT_DATA_DIR**: Path to dodot's data directory (~/.local/share/dodot) - **DODOT_PACK**: Name of the current pack being installed - **HOME**: User's home directory - **PATH**: System PATH (scripts can modify for their session)
Effects on User Environment ¶
- **Creates**: Whatever the script creates (directories, files, downloads) - **Modifies**: Whatever the script modifies (configs, permissions) - **Installs**: External tools, dependencies, packages - **Reversible**: Depends on script implementation - **Backups**: Script's responsibility to create if needed
Options ¶
Currently, InstallScriptPowerUp accepts no configuration options. All behavior is controlled by the script itself.
Script Template ¶
Use `dodot new install <pack>` to create a script from template:
#!/usr/bin/env bash set -euo pipefail echo "Installing <pack>..." # Your installation commands here # Access $DOTFILES_ROOT, $DODOT_PACK, etc. echo "Installation complete!"
Example End-to-End Flow ¶
User runs: `dodot install`
1. dodot finds ~/dotfiles/vim/ pack with install.sh 2. FileNameTrigger matches install.sh against pattern 3. InstallScriptPowerUp checks sentinel file 4. No sentinel or checksum mismatch: proceeds with execution 5. Copies install.sh to ~/.local/share/dodot/install/vim/ 6. Makes script executable 7. Executes with DODOT_PACK=vim, DOTFILES_ROOT=/home/user/dotfiles 8. Script installs vim plugins, creates ~/.vim directories 9. On success, stores SHA256 checksum in sentinel file 10. Future runs skip this script unless modified or --force used
Error Handling ¶
Common errors and their codes:
- **INST001**: Script file not found - **INST002**: Script execution failed (non-zero exit) - **INST003**: Script timeout (exceeds 5 minutes) - **INST004**: Permission denied (can't make executable) - **INST005**: Sentinel write failed
Best Practices ¶
1. **Use set -euo pipefail**: Fail fast on errors 2. **Check prerequisites**: Verify required tools exist 3. **Be idempotent**: Scripts should be safe to run multiple times 4. **Log progress**: Use echo statements for visibility 5. **Handle errors**: Provide clear error messages 6. **Document dependencies**: Comment what the script installs 7. **Use DODOT_PACK**: Reference pack name for pack-specific paths
Common Use Cases ¶
- **Language setup**: Install nvm, rbenv, pyenv - **Tool installation**: Download binaries not in package managers - **Plugin management**: Install vim/emacs/tmux plugins - **Directory creation**: Set up ~/.config structures - **Font installation**: Download and install custom fonts - **Credentials setup**: Initialize password stores, SSH keys
Comparison with Other PowerUps ¶
- **SymlinkPowerUp**: For configuration files (deploy phase) - **BrewPowerUp**: For Homebrew packages (also install phase) - **InstallScriptPowerUp**: For custom setup logic (install phase) - **ProfilePowerUp**: For shell configuration (deploy phase)
Use InstallScriptPowerUp when you need custom logic that package managers can't handle, or when you need to perform complex multi-step installations.
Index ¶
- Constants
- func GetInstallSentinelPath(pack string, pathsInstance *paths.Paths) string
- func NewInstallScriptPowerUp() types.PowerUp
- func RegisterInstallScriptPowerUpFactory()
- type InstallScriptPowerUp
- func (p *InstallScriptPowerUp) Description() string
- func (p *InstallScriptPowerUp) GetTemplateContent() string
- func (p *InstallScriptPowerUp) Name() string
- func (p *InstallScriptPowerUp) Process(matches []types.TriggerMatch) ([]types.Action, error)
- func (p *InstallScriptPowerUp) RunMode() types.RunMode
- func (p *InstallScriptPowerUp) ValidateOptions(options map[string]interface{}) error
Constants ¶
const (
// InstallScriptPowerUpName is the unique name for the install script power-up
InstallScriptPowerUpName = "install_script"
)
Variables ¶
This section is empty.
Functions ¶
func GetInstallSentinelPath ¶
GetInstallSentinelPath returns the path to the sentinel file for a pack Deprecated: Use pathsInstance.SentinelPath("install", pack) instead
func NewInstallScriptPowerUp ¶
NewInstallScriptPowerUp creates a new instance of the install script power-up
func RegisterInstallScriptPowerUpFactory ¶
func RegisterInstallScriptPowerUpFactory()
RegisterInstallScriptPowerUpFactory registers the install script power-up factory
Types ¶
type InstallScriptPowerUp ¶
type InstallScriptPowerUp struct{}
InstallScriptPowerUp runs install.sh scripts
func (*InstallScriptPowerUp) Description ¶
func (p *InstallScriptPowerUp) Description() string
Description returns a human-readable description of what this power-up does
func (*InstallScriptPowerUp) GetTemplateContent ¶
func (p *InstallScriptPowerUp) GetTemplateContent() string
GetTemplateContent returns the template content for this power-up
func (*InstallScriptPowerUp) Name ¶
func (p *InstallScriptPowerUp) Name() string
Name returns the unique name of this power-up
func (*InstallScriptPowerUp) Process ¶
func (p *InstallScriptPowerUp) Process(matches []types.TriggerMatch) ([]types.Action, error)
Process takes install script matches and generates install actions
func (*InstallScriptPowerUp) RunMode ¶
func (p *InstallScriptPowerUp) RunMode() types.RunMode
RunMode returns whether this power-up runs once or many times
func (*InstallScriptPowerUp) ValidateOptions ¶
func (p *InstallScriptPowerUp) ValidateOptions(options map[string]interface{}) error
ValidateOptions checks if the provided options are valid for this power-up