This task list outlines the steps to build your Go-based CLI tool, ralph, for managing dotfiles, rc files, shell tools, and helper functions, inspired by Starship and using Cobra. Your project will be github.com/mad01/ralph.
- Initialize Go Module:
- Run
go mod init github.com/mad01/ralphin your project's root directory.
- Run
- Project Structure:
- Define a clear directory structure. A common approach:
cmd/ralph/- Main application package for theralphCLI.internal/- Private application and library code (not intended for import by other projects).internal/config- Configuration loading and management.internal/dotfile- Logic for managing dotfiles (symlinking, templating).internal/shell- Logic for interacting with shell rc files and managing functions/aliases.internal/tool- Logic for managing external tools.
pkg/- Public library code, reusable by other projects.pkg/pipeutil- Your helper package for stdin/stdout shell binaries.
configs/examples/- Exampleralphconfiguration files.scripts/- Helper scripts for building, testing, etc.
- Define a clear directory structure. A common approach:
- Integrate Cobra CLI:
- Add Cobra:
go get -u github.com/spf13/cobra@latest - Create the root command (
ralph) incmd/ralph/main.goandcmd/ralph/commands/root.go. - Define initial subcommands using Cobra (e.g.,
init,apply,add,list,doctor).cmd/ralph/commands/cmd_init.gocmd/ralph/commands/cmd_apply.gocmd/ralph/commands/cmd_add.go(placeholder)cmd/ralph/commands/cmd_list.gocmd/ralph/commands/cmd_doctor.go
- Add Cobra:
- Basic Logging/Output:
- Implement a simple logging mechanism for user feedback.
- Consider using the standard
logpackage initially, or a library likegithub.com/charmbracelet/logfor more styled output later. (Usedfmtandlog,charmbracelet/logis a future enhancement)
- Define Configuration Structure:
- The configuration file format will be TOML.
- Define Go structs in
internal/config/types.goto represent your configuration (e.g.,config.go). This might include:- Path to the user's dotfiles source repository.
- A list/map of dotfiles to manage (source relative to repo, target path on system).
- Definitions for "standard tools" (name, install check command, config files).
- Definitions for shell aliases and functions.
- Configuration Loading:
- Implement logic in
internal/config/load.goto find and load theconfig.tomlfile. - Default location:
$XDG_CONFIG_HOME/ralph/config.tomlor~/.config/ralph/config.toml. - Note: Users should be encouraged to keep their actual
config.tomlwithin their version-controlled dotfiles repository and symlink it to this default location.ralph initcould assist with this. - Consider using
github.com/spf13/viperfor configuration handling (it supports TOML well) or a dedicated TOML parser likegithub.com/BurntSushi/toml. (UsedBurntSushi/toml)
- Implement logic in
- Default Configuration:
- Provide a way to generate a default
config.toml(e.g., as part of theralph initcommand). Store a template inconfigs/examples/default.config.toml. (initCmdgenerates a minimal one, can be improved to use the file or embed)
- Provide a way to generate a default
- Configuration Validation:
- Implement checks in
internal/config/validate.goto ensure the loadedconfig.tomlis valid (e.g., required fields are present, paths exist).
- Implement checks in
- Dotfile Specification in Config:
- Define how users specify which dotfiles to manage in
config.toml. Example:[dotfiles.bashrc] source = ".bashrc" # Relative to dotfiles source repository target = "~/.bashrc" is_template = true # Optional [dotfiles.nvim_config] source = "nvim/init.vim" target = "~/.config/nvim/init.vim"
- Note: The
config.tomlitself, defining these mappings, is best version-controlled alongside the actual dotfiles in the user's source repository.
- Define how users specify which dotfiles to manage in
- Symlinking Engine (
internal/dotfile/symlink.go):- Implement logic to create symbolic links from the source dotfiles repository to their target locations.
- Handle path expansion (e.g.,
~to home directory). - Manage existing files at target locations: provide options (backup, overwrite, skip - configurable or via flags). (Basic backup implemented, flags are TODO)
- Templating (Optional but Recommended):
- Consider using Go's
text/templatepackage for dynamic content in dotfiles. (Implemented) - Allow users to define template variables in their
ralphconfig.tomlor use environment variables. (Basic env var access done, config variables TODO) - Process templates before symlinking. (Implemented)
- Consider using Go's
-
ralph applyCommand (cmd/ralph/commands/cmd_apply.go):- This command will be the core workhorse.
- It should read the
config.toml, then iterate through specified dotfiles and:- Process templates (if any).
- Create/update symlinks.
- Apply shell configurations (see next section).
- RC File Snippet Injection (
internal/shell/rc_manager.go):- Develop a robust method to add necessary sourcing lines or configurations to shell rc files (e.g.,
~/.bashrc,~/.zshrc,~/.config/fish/config.fish). - This is crucial for sourcing generated alias/function files or initializing tools.
- Use markers to manage the block of code
ralphadds, ensuring idempotency (e.g.,# BEGIN RALPH MANAGED BLOCK...# END RALPH MANAGED BLOCK).
- Develop a robust method to add necessary sourcing lines or configurations to shell rc files (e.g.,
- Define "Standard Tools" in Config:
- Allow users to define tools they want
ralphto manage or ensure are set up inconfig.toml. - Example in
config.toml:[[tools]] name = "fzf" check_command = "command -v fzf" # How to check if installed install_hint = "Install fzf from [https://github.com/junegunn/fzf](https://github.com/junegunn/fzf)" # Optionally, specify config files for this tool that ralph should manage # config_files = [ { source = "fzf/.fzfrc", target = "~/.fzfrc" } ]
- Allow users to define tools they want
-
ralph add tool <tool_name>Command (Consider for later, focus on config-driven first):- This could be a helper to add predefined tool configurations to the user's
ralph.toml. (Placeholder command exists)
- This could be a helper to add predefined tool configurations to the user's
- Shell Function/Alias Management (
internal/shell/functions.go):- Allow users to define custom shell functions or aliases in
config.toml.[shell.aliases] ll = "ls -alh" [shell.functions.myfunc] body = """ echo "Hello from myfunc!" echo "Arguments: $@" """
ralph applyshould generate a script (e.g.,~/.config/ralph/generated_aliases.sh,~/.config/ralph/generated_functions.sh).- The RC file snippet injection (from Part III) should source these generated scripts.
- Allow users to define custom shell functions or aliases in
- Go Function Integration (as
ralphsubcommands):- Design how Go functions within
ralphitself can be exposed as utility subcommands. (Cobra structure in place) - These are not shell functions but actual Go code executed by
ralph. - Example:
ralph system-infocould be a subcommand written in Go.
- Design how Go functions within
- Package Design (
pkg/pipeutil/pipeutil.go):- Create this new Go package:
github.com/mad01/ralph/pkg/pipeutil.
- Create this new Go package:
- Stdin Handling:
- Provide utility functions to easily read all of
os.Stdin(e.g.,ReadAll() ([]byte, error)). - Consider line-by-line reading utilities (
Scanner() *bufio.Scanner).
- Provide utility functions to easily read all of
- Stdout Handling:
- Provide utility functions to easily write to
os.Stdout(e.g.,Print(data []byte),Println(s string)).
- Provide utility functions to easily write to
- Stderr Handling:
- Provide utility functions for writing formatted error messages to
os.Stderr(e.g.,Error(err error),Errorf(format string, a ...any)).
- Provide utility functions for writing formatted error messages to
- Simplified Argument Access (Optional):
- While the main
ralphCLI uses Cobra, standalone binaries built with thispipeutilpackage might benefit from very simple flag/argument helpers if they are not complex enough to warrant Cobra themselves. Focus on stdin/stdout first.
- While the main
- Standardized Exit Codes:
- Encourage or provide constants for common exit codes (e.g.,
ExitSuccess = 0,ExitFailure = 1).
- Encourage or provide constants for common exit codes (e.g.,
- Example Usage/Templates:
- Create a clear example Go program in
pkg/pipeutil/example/demonstrating how to use this package to build a simple filter/transformer. - Document how to compile and use these standalone binaries.
- Create a clear example Go program in
-
ralph initCommand (cmd/ralph/commands/cmd_init.go):- Guides new users:
- Creates a default
config.tomlin the appropriate config directory (e.g.,$XDG_CONFIG_HOME/ralph/config.toml). (Minimal config created, see TODO for improvement) - Asks for the location of their dotfiles source repository (e.g.,
~/.dotfiles_src). - Advises the user to commit their dotfiles source repository (including the
ralphconfig.tomlif they choose to place it there and symlink it) to version control (e.g., Git). - Optionally, offer to symlink
config.tomlif found in a conventional location within the user's dotfiles source repository to the expected$XDG_CONFIG_HOME/ralph/config.toml. (Skipped for now, advised manually) - Provides instructions on next steps (e.g., "Populate your dotfiles repository, customize
config.toml, then runralph apply").
- Creates a default
- Guides new users:
-
ralph listCommand (cmd/ralph/commands/cmd_list.go):- Displays:
- Managed dotfiles and their symlink status (e.g., linked, missing source, target conflict).
- Configured tools and their detected status (installed/not installed). (Basic list, status check TODO)
- Defined shell functions/aliases.
- Displays:
-
ralph doctorCommand (Status Check -cmd/ralph/commands/cmd_doctor.go):- Checks the health of the
ralphsetup:- Verifies
config.tomlreadability and validity. - Checks for broken symlinks.
- Verifies if rc file snippets are correctly sourced (e.g., by checking for the presence of the generated function files in
PATHor specific environment variables). (Basic RC block check, deeper check TODO)
- Verifies
- Checks the health of the
-
--dry-runFlag:- Add a global persistent flag or a flag for commands like
applyto show what changes would be made without actually making them. (Flag added, full implementation TODO)
- Add a global persistent flag or a flag for commands like
- Colorized Output:
- Use a library (e.g.,
github.com/fatih/colororgithub.com/charmbracelet/lipgloss) for better visual feedback in the terminal. (Demonstrated, wider application TODO)
- Use a library (e.g.,
- Interactive Prompts (where appropriate):
- For actions like overwriting files, consider using a library like
github.com/AlecAivazis/survey/v2. (Used ininitCmd)
- For actions like overwriting files, consider using a library like
- Unit Tests:
- Write unit tests for critical logic:
- Config parsing (TOML) and validation (
internal/config). (Started forExpandPath) - Symlinking logic, especially edge cases (
internal/dotfile). (TODO) - Template execution. (TODO)
pkg/pipeutilfunctions. (TODO)
- Config parsing (TOML) and validation (
- Write unit tests for critical logic:
- Integration Tests (Basic):
- Consider simple integration tests for CLI commands.
- This might involve setting up a temporary home directory, running
ralphcommands usingos/exec, and then asserting file system state or command output.
- README.md:
- Create a comprehensive
README.mdforgithub.com/mad01/ralph:- Project overview, philosophy, and goals.
- Installation instructions (Go install, binaries from releases).
- Detailed configuration guide with examples (using TOML).
- Best practices for version controlling the dotfiles repository and the
config.toml. - Usage examples for all commands and flags.
- How to use the
pkg/pipeutilpackage to build custom tools.
- Create a comprehensive
- Contributing Guidelines (CONTRIBUTING.md):
- If you plan for others to contribute (coding style, PR process).
- GoDoc:
- Write clear GoDoc comments for all public functions and packages, especially for
pkg/pipeutil. (Partially done, needs thorough pass)
- Write clear GoDoc comments for all public functions and packages, especially for
- Makefile / Build Scripts (
scripts/build.sh,Makefile):- Create scripts or a
Makefilefor common development tasks:make buildmake testmake lint(usinggolangci-lint)make format(usinggofmtorgoimports)
- Create scripts or a
- Cross-Compilation (Optional but good for distribution):
- Set up
goreleaseror use Go's built-in cross-compilation features to build binaries for different OS/architectures (Linux, macOS, Windows).
- Set up
- GitHub Releases:
- Set up GitHub Actions to automate building releases when you tag a new version.
goreleaseris excellent for this.
- Modularity: Design
ralphso that different functionalities (dotfile linking, shell setup, tool management) are as decoupled as possible. - Configuration over Code: Emphasize defining behavior through the
config.tomlfile. - Speed & Efficiency: Keep performance in mind, especially for any part of
ralphthat might be involved in shell startup (e.g., sourcing generated files). (Ongoing consideration) - Clear Diagnostics: When things go wrong, provide helpful, actionable error messages (the
ralph doctorcommand is key here). - Extensibility (Future): Think about how users might eventually be able to add their own "plugin" behaviors (though this is likely beyond the initial scope).
- Lifecycle Hook System: Implement a hooks system that allows users to run custom scripts at key points in the ralph lifecycle.
- Custom Template Functions: Extend the templating system to allow users to register custom template functions.
- Platform-Specific Configurations: Allow users to define platform-specific variations of dotfiles.
- Plugin System: Design a more formal plugin system where Go packages can extend ralph's functionality.
-
ralph initImprovement: Usego:embedforconfigs/examples/default.config.tomlto makeinitCmdmore robust in creating the initial config file. - Tool Status Check: Implement the logic to run
tool.CheckCommandincmd_list.goandcmd_doctor.go. -
applyCmdFlags: Implement--overwriteand--skipflags forralph applyto control symlink behavior. - Full
--dry-runImplementation: PropagatedryRunstatus to file operation functions (symlinking, writing files, RC management) so they only print intended actions. - Wider Colorization: Apply
fatih/colormore broadly across CLI output for better visual feedback. - Template Variables from Config: Allow users to define variables in
config.tomlfor use in templates. - Unit Tests Expansion: Add more unit tests for:
internal/config(parsing, full validation).internal/dotfile(symlinking edge cases, templating).internal/shell(RC management, alias/function generation).pkg/pipeutil.
-
ralph doctorRC Sourcing Check: Enhance the check for RC file sourcing to verify that the sourced script files (aliases, functions) actually exist. - Error Handling & User Feedback: Review and improve error messages and user feedback across all commands.
-
ralph add toolImplementation: Flesh out theralph add tool <tool_name>command. - CONTRIBUTING.md: Create if project aims for external contributions.
- LICENSE file: Add a license file (e.g., MIT).
- Further Extensibility Features:
- Enhanced Hook System: Add more hook points (e.g., pre/post shell config, pre/post tool check).
- Custom Template Functions: Allow users to define shell scripts that can be called as functions within templates.
- Platform Detection & Conditional Configurations: Add OS/platform detection to enable conditional dotfile handling.
- Remote Scripts Support: Add capability to download and execute remote hook scripts (with appropriate security measures).
- Hook Timeouts and Error Handling: Implement timeout and retry mechanisms for hooks.
- Sandbox Environment Updates: Extend the sandbox environment to include examples of the hook system in action.
- Documentation for Hooks: Add comprehensive documentation for the hook system with examples.
This list is quite detailed to give you a good starting point. Remember to break these down into smaller, manageable tasks as you go. Good luck with building ralph!