Initializers¶
Initializers are responsible for setting up the CMDR environment during the cmdr init command. They configure the filesystem, shell profiles, and other necessary components.
Initializer Interface¶
The core interface is simple1:
The isUpgrade parameter indicates whether this is a fresh installation or an upgrade of an existing installation.
Factory Registration¶
Initializers register themselves via factory pattern2:
core.RegisterInitializerFactory("filesystem", func(cfg Configuration) (Initializer, error) {
return NewFilesystemInitializer(cfg), nil
})
Initializer Implementations¶
Filesystem Initializers¶
Source: core/initializer/filesystem.go
FSBackup¶
Backs up existing profile directory before modifications3:
Usage: Registered as "profile-dir-backup"
EmbedFSExporter¶
Exports embedded filesystem to disk4:
type EmbedFSExporter struct {
filesystem fs.FS // Embedded filesystem
srcPath string // Source path in embed
dstPath string // Destination path on disk
fileMode os.FileMode // File permissions
}
Usage: Registered as "profile-dir-export" - exports shell profile scripts from embedded assets.
DirRender¶
Renders Go templates in a directory5:
type DirRender struct {
data interface{} // Template data
srcPath string // Directory to render
ext string // Template extension (e.g., ".gotmpl")
}
Usage: Registered as "profile-dir-render" - renders profile templates with configuration.
Profile Initializer¶
Source: core/initializer/profile.go
ProfileInjector¶
Injects CMDR initialization script into shell profile6:
type ProfileInjector struct {
scriptPath string // Path to cmdr_initializer.sh
profilePath string // Path to shell profile (~/.bashrc, ~/.zshrc, etc.)
}
Key Operations:
- Detect Shell Profile7:
- Bash →
~/.bashrc - Zsh →
~/.zshrc - Fish →
~/.config/fish/config.fish -
Ash/Sh →
~/.profile -
Generate Profile Statement:
-
Update Profile:
- Reads existing profile
- Replaces old CMDR source line (if exists)
- Adds source line (if not exists)
- Writes updated profile
Shell Detection: Uses parent process inspection to determine the current shell.
Command Initializer¶
Source: core/initializer/command.go
Registers CMDR itself as a managed command, enabling cmdr upgrade.
Database Initializer¶
Source: core/initializer/database.go
Initializes the BoltDB database schema.
Initialization Flow¶
When cmdr init is run, initializers execute in this order:
1. profile-dir-backup
└─> Backup existing profile directory
2. binary (BinaryManager.Init)
└─> Create bin/ and shims/ directories
3. profile-dir-export
└─> Export embedded profile scripts
4. profile-dir-render
└─> Render profile templates with config
5. profile-injector
└─> Add source line to shell profile
6. database
└─> Initialize database schema
7. command
└─> Register cmdr as managed command
Embedded Assets¶
CMDR embeds shell profile scripts using Go 1.16+ embed:
The embedded profile directory contains:
These are extracted to ~/.cmdr/profile/ and rendered with configuration values.
Template Rendering¶
Templates use Go's text/template package with configuration data:
// Example template
// cmdr_initializer.sh.gotmpl
export CMDR_ROOT="{{.Configuration.GetString "core.root_dir"}}"
export PATH="{{.Configuration.GetString "core.bin_dir"}}:$PATH"
Rendered output:
Safety Features¶
- Backup Before Modify: Profile directory is backed up before changes
- Idempotent Injection: Multiple
cmdr initcalls don't duplicate source lines - Version Migration: Old format source lines are updated to new format
- Error Recovery: Failures during init don't leave system in broken state
Testing Initializers¶
Initializers can be tested independently:
// Example test
cfg := core.NewConfiguration()
cfg.Set(core.CfgKeyCmdrProfileDir, "/tmp/test-profile")
injector := NewProfileInjector("/tmp/test-profile/init.sh", "/tmp/.bashrc")
err := injector.Init(false)
-
Initializer interface in
core/initializer.goL5-L7 ↩ -
Factory registration pattern in
core/initializer.goL18-L20 ↩ -
FSBackup implementation in
core/initializer/filesystem.goL21-L61 ↩ -
EmbedFSExporter implementation in
core/initializer/filesystem.goL63-L146 ↩ -
DirRender implementation in
core/initializer/filesystem.goL148-L264 ↩ -
ProfileInjector implementation in
core/initializer/profile.goL20-L118 ↩ -
Shell detection in
core/initializer/profile.goL120-L151 ↩