| name | dotfiles-installer-dev |
| description | Development guide for the dotfiles-installer project. Use when working with package managers (brew, apt, dnf), implementing interfaces, using the commander/logger/filesystem utilities, handling privilege escalation, or understanding the codebase architecture. Covers project structure, key interfaces, configuration files, and development commands. For general Go coding conventions and testing patterns, use the go-dev skill. |
Dotfiles Installer Development
Development guide for the dotfiles-installer Go codebase.
Project Structure
installer/
├── main.go # Entry point (version injection)
├── cmd/ # CLI commands (Cobra)
│ ├── root.go # Root command, global setup
│ ├── install.go # Main installation workflow
│ ├── checkCompatibility.go # Compatibility check command
│ └── version.go # Version display
├── lib/ # Core business logic
│ ├── compatibility/ # OS/distro detection
│ ├── pkgmanager/ # Package manager interface
│ ├── brew/ # Homebrew implementation
│ ├── apt/ # APT implementation
│ ├── dnf/ # DNF implementation
│ ├── gpg/ # GPG key management
│ ├── shell/ # Shell installation
│ ├── dotfilesmanager/ # Chezmoi integration
│ └── packageresolver/ # Package name resolution
├── utils/ # Shared utilities
│ ├── logger/ # Logging with progress display
│ ├── osmanager/ # OS operations interface
│ ├── privilege/ # Sudo/doas escalation
│ ├── commander.go # Command execution
│ ├── filesystem.go # File operations
│ └── httpclient/ # HTTP client interface
├── cli/ # Interactive UI components
├── internal/config/ # Embedded YAML configs
├── Taskfile.yml # Task runner commands
└── .goreleaser.yaml # Release configuration
Key Interfaces
Commander (utils/commander.go)
Execute system commands with functional options:
result, err := commander.RunCommand(ctx, "brew", []string{"install", pkg},
WithCaptureOutput(),
WithEnv(env),
WithTimeout(5 * time.Minute),
)
Available options: WithEnv(), WithDir(), WithInput(), WithCaptureOutput(), WithDiscardOutput(), WithInteractive(), WithTimeout(), WithStdout(), WithStderr()
Logger (utils/logger/)
Logging with progress tracking:
logger.StartProgress("Installing packages")
logger.UpdateProgress("Installing git")
logger.FinishProgress()
// or
logger.FailProgress(err)
Methods: Trace, Debug, Info, Success, Warning, Error
FileSystem (utils/filesystem.go)
File operations interface for testability:
exists, err := fs.PathExists(path)
data, err := fs.ReadFile(path)
err = fs.WriteFile(path, data, 0644)
err = fs.CreateDirectory(path)
OsManager (utils/osmanager/)
OS-level operations (user management, environment, program queries, permissions).
Privilege Escalator (utils/privilege/)
Smart privilege escalation (prefers sudo, falls back to doas):
escalatedCmd, escalatedArgs, err := escalator.EscalateCommand("apt-get", []string{"install", "git"})
isRoot := escalator.IsRunningAsRoot()
Development Commands
| Command | Purpose |
|---|---|
task build |
Build binary via goreleaser |
task test |
Run tests with race detection |
task fmt |
Format code (gofumpt, goimports, golines) |
task lint |
Run golangci-lint and typos |
task check |
Run tests + lint |
task cov |
Generate coverage report |
task bench |
Run benchmarks |
task sloc |
Print lines of code stats |
Adding Features
New Package Manager
- Create package in
lib/{managername}/ - Implement
PackageManagerinterface (see lib/pkgmanager/pkgmanager.go) - Add installer interface if installation needed
- Add unit and integration tests
- Update
internal/config/packagemap.yamlfor package name mappings
New Utility Interface
- Define interface in
utils/ - Create implementation with constructor
- Generate mock: run
mockeryin project root - Inject via constructors where needed
New CLI Command
- Create file in
cmd/ - Define
cobra.Commandwith flags - Register in
root.goinit() - Implement run logic using injected dependencies
Configuration Files
compatibility.yaml (internal/config/)
Defines supported OS/distros, architectures, and prerequisites:
supported_os:
darwin:
name: macOS
distros:
- name: macOS
architectures: [amd64, arm64]
prerequisites: [git, curl]
packagemap.yaml (internal/config/)
Maps generic package codes to manager-specific names:
packages:
git:
brew: git
apt: git
dnf: git
neovim:
brew: neovim
apt: neovim
dnf: neovim