@sebastienrousseau/dotfiles
Version:
The Trusted Shell Platform — Universal dotfiles managed by Chezmoi. Features Bash & Zsh for macOS, Linux & WSL. Rust modern tooling & enterprise-grade security.
172 lines (128 loc) • 5.15 kB
Markdown
---
render_with_liquid: false
---
# ADR-004: Chezmoi + Custom CLI Wrapper Architecture
**Status**: Accepted
**Date**: 2026-02-09
**Authors**: @sebastienrousseau
## Context
Managing dotfiles requires:
- Tracking file changes and applying them consistently
- Handling platform-specific configurations
- Supporting encrypted secrets
- Providing a good developer experience
Options considered:
1. **Bare git repository**: Simple but poor UX, no templating
2. **GNU Stow**: Symlink-based, limited features
3. **Chezmoi only**: Powerful but complex CLI
4. **Custom from scratch**: High maintenance burden
5. **Chezmoi + wrapper**: Best of both worlds
## Decision
Use **Chezmoi as the core engine** with a **custom `dot` CLI wrapper** that:
### Architecture
```text
┌─────────────────────────────────────────────┐
│ dot CLI │
│ (User-friendly interface, custom commands) │
├─────────────────────────────────────────────┤
│ Command Modules │
│ core │ diagnostics │ tools │ appearance │
│ secrets │ security │ meta │
├─────────────────────────────────────────────┤
│ Shared Library │
│ utils.sh (resolve_source_dir, run_script) │
├─────────────────────────────────────────────┤
│ Chezmoi │
│ (Template engine, state management, apply) │
└─────────────────────────────────────────────┘
```
### Core Principles
**1. Chezmoi handles complexity:**
- Template rendering with Go text/template
- Encrypted secrets with age
- State tracking (what's applied vs source)
- Cross-platform path handling
**2. dot CLI handles UX:**
- Memorable command names (`dot sync` vs `chezmoi apply`)
- Domain-specific commands (`dot doctor`, `dot theme`)
- Integration with external tools (Nix, Docker, Neovim)
- Consistent help and error messages
**3. Modular command structure:**
```text
scripts/dot/
├── lib/
│ └── utils.sh # Shared functions
└── commands/
├── core.sh # apply, sync, update, add, diff
├── diagnostics.sh # doctor, heal, health, benchmark
├── tools.sh # tools, new, packages
├── appearance.sh # theme, wallpaper, fonts
├── secrets.sh # secrets-init, secrets
├── security.sh # firewall, backup, encrypt-check
└── meta.sh # upgrade, docs, learn
```
**4. Delegation pattern:**
```bash
# Main dispatcher in dot CLI
dispatch() {
local module="$1" cmd="$2"
shift 2
exec bash "$src_dir/scripts/dot/commands/$module.sh" "$cmd" "$@"
}
```
### Chezmoi Integration Points
| Feature | Chezmoi | dot CLI |
|---------|---------|---------|
| Apply changes | `chezmoi apply` | `dot sync` |
| View diff | `chezmoi diff` | `dot diff` |
| Edit secrets | `chezmoi edit --encrypted` | `dot secrets` |
| Source directory | `chezmoi source-path` | `dot cd` |
| Health check | `chezmoi doctor` | `dot doctor` (extended) |
### Extension Points
Custom commands can:
1. Wrap chezmoi commands with better defaults
2. Add entirely new functionality (benchmarks, themes)
3. Integrate with system tools (nix, docker, brew)
4. Provide interactive experiences (tour, learn)
## Consequences
### Positive
- Leverage Chezmoi's battle-tested engine
- User-friendly interface for common tasks
- Easy to add domain-specific commands
- Modular structure enables testing and maintenance
- Single entry point (`dot`) for all operations
### Negative
- Two layers to understand (chezmoi + dot)
- Version coupling between chezmoi and scripts
- Some chezmoi features not exposed via dot
### Neutral
- Advanced users can still use chezmoi directly
- Documentation needed for both layers
- Upgrade path when chezmoi adds new features
## Implementation Notes
### Adding a New Command
1. Identify the appropriate module (or create new one)
2. Add function `cmd_<name>()` to module
3. Add case to module's dispatch
4. Add case to main dot CLI dispatcher
5. Update help text
6. Add tests if complex
### Module Template
```bash
#!/usr/bin/env bash
# Dotfiles CLI - <Category> Commands
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../lib/utils.sh"
cmd_example() {
# Implementation
}
case "${1:-}" in
example) shift; cmd_example "$@" ;;
*) echo "Unknown command: ${1:-}" >&2; exit 1 ;;
esac
```
## References
- [Chezmoi Documentation](https://www.chezmoi.io/)
- [Command Pattern](https://refactoring.guru/design-patterns/command)
- [Unix Philosophy](https://en.wikipedia.org/wiki/Unix_philosophy)