This is a Ruby project using Rubocop for code formatting and style enforcement.
- Run tests:
bundle exec rspecorrake test - Format code:
rubocop --fix - Check code style:
rubocop - Install dependencies:
bundle install
- Start development server:
rails serverorbundle exec rails s - Run console:
rails consoleorbundle exec rails c - Database migrations:
rails db:migrate - Run specific tests:
bundle exec rspec spec/path/to/test_spec.rb
- Follow Rubocop defaults and project-specific
.rubocop.ymlconfiguration - Use 2-space indentation
- Use snake_case for methods and variables
- Use CamelCase for classes and modules
- Prefer single quotes for strings unless interpolation is needed
- Use trailing commas in multiline arrays and hashes
app/- Main application code (models, controllers, views, etc.)spec/ortest/- Test filesconfig/- Configuration fileslib/- Library codedb/- Database migrations and schema
- Test files should end with
_spec.rb - Use descriptive test names with
describeanditblocks - Follow the "Given-When-Then" pattern for test structure
- Use factories (FactoryBot) for test data
- Mock external dependencies when appropriate
- Run all tests:
bundle exec rspec - Run specific file:
bundle exec rspec spec/models/user_spec.rb - Run specific line:
bundle exec rspec spec/models/user_spec.rb:15 - Run with coverage:
COVERAGE=true bundle exec rspec
- Dependencies are managed in
Gemfile - Run
bundle installafter adding new gems - Use
bundle execto run commands with the correct gem versions - Development/test gems should be in appropriate groups
- Read existing code to understand patterns and conventions
- Write tests for new functionality
- Implement the feature following Rubocop style
- Format code: Always run
rubocop --fixafter editing files - Run tests: Verify changes with
bundle exec rspec - Check for regressions: Run full test suite
- Always run
rubocop --fixbefore committing changes - Ensure all tests pass before marking work complete
- Follow existing patterns in the codebase
- Add proper error handling and validation
- Write clean, readable code without unnecessary comments
- Model files:
app/models/user.rb - Controller files:
app/controllers/users_controller.rb - Test files:
spec/models/user_spec.rb,spec/controllers/users_controller_spec.rb - Helper files:
app/helpers/users_helper.rb
- Never commit secrets, API keys, or credentials
- Use environment variables for sensitive configuration
- Follow Rails security best practices
- Validate user input and use parameter sanitization
- Follow RESTful conventions for controllers
- Use strong parameters in controllers
- Keep business logic in models or service objects
- Use concerns for shared functionality
- Follow the "fat models, skinny controllers" principle
- Use migrations for schema changes
- Add indexes for performance on foreign keys and frequently queried columns
- Use validations in models
- Follow ActiveRecord conventions
This guide helps AI assistants understand the project structure, coding standards, and development workflow for this Ruby project.
This project uses MItamae for configuration management. Cookbooks are located in
cookbooks/ directory.
Quick reference:
bd ready- Find unblocked workbd create "Title" --type task --priority 2- Create issuebd close <id>- Complete workbd sync- Sync with git (run at session end)
# cookbooks/example/default.rb
# Helper methods can be defined in a module
module ExampleHelper
def example_installed_version
# Check installed version logic
# Return version string or nil if not installed
end
def example_latest_version
# Fetch latest version from external source
# Return version string or nil if unavailable
end
end
::MItamae::RecipeContext.include ExampleHelper
::MItamae::ResourceContext.include ExampleHelper
# Main recipe logic
case node[:platform]
when "debian", "ubuntu", "mint", "pop"
# Linux-specific installation
home = node[:home]
user = node[:user]
# Install using platform-specific package manager or direct download
when "darwin"
# macOS-specific installation (Homebrew)
package "example-tool"
when "windows"
# Windows-specific installation
log "Not implemented"
endFor tools that need version checking and automatic updates:
- Define helper methods to check installed and latest versions
- Use
version_less_than?from PlatformHelpers for semantic version comparison - Download only when needed to avoid unnecessary network calls
- Use cache directory (
~/.cache/) instead of/tmpfor user-specific downloads - Add error handling with rescue blocks for network/execution failures
Example from cookbooks/rpi_imager/default.rb:
def rpi_imager_installed_version
begin
case node[:platform]
when "debian", "ubuntu", "mint", "pop"
appimage_path = "#{node[:home]}/.local/bin/rpi-imager"
if File.exist?(appimage_path)
result = run_command("#{appimage_path} --version 2>/dev/null", error: false)
if result.success?
match = result.stdout.match(/v(\d+\.\d+\.\d+)/)
return match[1] if match
end
end
# ... other platforms
end
rescue => e
MItamae.logger.warn "Failed to get installed version: #{e.message}"
end
nil
end
def rpi_imager_latest_appimage_info
begin
max_retries = 3
retry_count = 0
while retry_count < max_retries
cmd = "curl -s https://downloads.raspberrypi.com/imager/"
result = run_command(cmd, error: false)
if result.success?
html = result.stdout
appimages = []
# Parse HTML for download links
html.scan(/href="(imager_\d+\.\d+\.\d+_amd64\.AppImage)"/) do |match|
filename = match[0]
version_match = filename.match(/imager_(\d+\.\d+\.\d+)_amd64\.AppImage/)
if version_match
appimages << {version: version_match[1], filename: filename}
end
end
# Find highest version
unless appimages.empty?
highest = appimages.first
appimages.each do |appimage|
if version_less_than?(highest[:version], appimage[:version])
highest = appimage
end
end
return highest
end
end
retry_count += 1
sleep 2 if retry_count < max_retries
end
rescue => e
MItamae.logger.warn "Failed to get latest version: #{e.message}"
end
nil
end- Use
directoryfor creating directories with proper permissions - Use
http_requestfor downloading files (notcurl/wgetin execute blocks) - Use
executewithnot_if/only_ifto make operations idempotent - Chain notifications (
notifies) for sequential operations - Set user ownership for user-specific files/directories
Always use guards (not_if/only_if) to ensure recipes are independent and
idempotent. Recipes should be able to run multiple times without errors, even if
resources already exist.
-
Check if file/directory exists:
execute "install something" do command "./install.sh" not_if { File.exist?("/path/to/installed/file") } end
-
Check if command is available:
execute "add repository" do command "apt-add-repository ppa:foo/bar" not_if "which apt-add-repository && apt-add-repository --list | grep foo/bar" end
-
Check if package is installed:
execute "install package from source" do command "make install" not_if "dpkg -l | grep package-name" end
-
Use only_if for conditional execution:
execute "configure macOS settings" do command "defaults write com.apple.finder ShowPathbar -bool true" only_if { node[:platform] == "darwin" } end
- No hard failures: If a file already exists, the recipe shouldn't crash
- Order doesn't matter: Recipes can be run in any sequence
- Safe re-runs: Users can safely re-run mitamae without breaking things
- Easier debugging: Each recipe is self-contained
| Scenario | Guard Pattern |
|---|---|
| Create directory | not_if { File.directory?(path) } |
| Create file from template | not_if { File.exist?(path) } |
| Install binary | not_if "which binary_name" |
| Clone git repo | not_if { File.directory?("#{path}/.git") } |
| Run install script | not_if { File.exist?(marker_file) } |
| Platform-specific | only_if { node[:platform] == "platform" } |
- Linux (deb-based):
"debian", "ubuntu", "mint", "pop" - macOS:
"darwin"or"osx" - Windows:
"windows"
- Idempotency: Ensure cookbooks can run multiple times without side effects
- Error handling: Wrap external commands and network calls in rescue blocks
- Logging: Use
MItamae.logger.info/warnfor debugging - User-specific paths: Use
node[:home]andnode[:user]variables - Cache management: Store downloads in user's cache directory
- Cleanup: Remove old installations when switching methods (e.g., snap → AppImage)
When writing system files (/etc, /usr/local, etc.):
-
Linux systems (Debian, Ubuntu, RedHat, Arch):
- Use
owner "root"andgroup "root"for system files - Example:
template "/etc/ssh/sshd_config"withowner "root",group "root"
- Use
-
macOS systems (Darwin):
- Use
owner "root"andgroup "wheel"for system files - However, when running mitamae without root privileges (default on macOS), omit
ownerandgroupto avoid chown permission errors - Example:
template "/etc/ssh/sshd_config"with onlymode "644"(no owner/group)
- Use
-
Windows systems:
- No ownership specification needed (Windows ACLs different)
- Use forward slashes in paths:
"C:/ProgramData/ssh/sshd_config"
-
sudo usage in execute commands:
- Use
sudoin command strings for operations requiring root - Use
user "root"attribute for execute resources when possible - Example:
execute "enable macOS ssh server"withcommand "sudo launchctl load -w ..."
- Use
-
Platform detection:
- Use
node[:platform]for platform-specific logic - Helper
windows?,wsl?available from PlatformHelpers
- Use
MRuby Restrictions:
- MItamae uses mruby (minimal Ruby) which doesn't support
requireorrequire_relative - All helper modules must be defined inline in
lib/recipe_helper.rb - Cannot load external Ruby files; use
include_recipeor embed modules directly
Cross-Platform Considerations:
- Unix/Linux/macOS: Use bash shell scripts (
incremental_backup_scripthelper) - Windows: Use PowerShell scripts (no generic helper, implement per-cookbook)
- Platform Detection: Always check
node[:platform]orwindows?before using platform-specific code - Path Separators: Use forward slashes (
/) even on Windows for Ruby paths - Shell Compatibility: Bash scripts won't work on Windows; PowerShell won't work on Unix
Example Pattern:
case node[:platform]
when "debian", "ubuntu", "mint", "pop", "redhat", "fedora", "arch", "darwin"
# Unix-like systems: use bash
execute "backup config" do
command incremental_backup_script("/etc/config/file.conf", ".backup")
user "root"
end
when "windows"
# Windows: use PowerShell
execute "backup config on Windows" do
command <<~EOH
powershell -Command "
# Windows-specific backup logic
"
EOH
end
endversion_less_than?(v1, v2): Compare semantic versionsgithub_latest_version(repo): Get latest GitHub release tagrun_command(cmd, error: false): Execute shell command safelysudo(user): Generate sudo command prefixincremental_backup_script(file_path, backup_suffix): Generate bash script for incremental backups (.backup,.backup.2, etc.)
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds