Contributing Guide
Thank you for your interest in contributing to Azu CLI! This guide will help you get started with contributing to the project.
Getting Started
Prerequisites
Before contributing, ensure you have:
Crystal 1.16.0 or later
Git for version control
A GitHub account for submitting pull requests
Basic knowledge of Crystal programming language
Development Setup
Fork the repository
# Fork on GitHub, then clone your fork git clone https://github.com/your-username/azu_cli.git cd azu_cli
Install dependencies
shards install
Set up development environment
# Create development configuration cp config/application.yml.example config/development.yml # Set up database (if needed) azu db:create azu db:migrate
Run tests
crystal spec
Development Workflow
1. Create a Feature Branch
# Create and switch to a new branch
git checkout -b feature/your-feature-name
# Or for bug fixes
git checkout -b fix/your-bug-description
2. Make Your Changes
Follow these guidelines when making changes:
Write tests for new functionality
Update documentation for any new features
Follow coding standards (see below)
Keep commits atomic and well-described
3. Test Your Changes
# Run all tests
crystal spec
# Run specific test file
crystal spec spec/your_test_spec.cr
# Run with coverage
crystal spec --coverage
# Format code
crystal tool format
# Check for issues
ameba
4. Submit a Pull Request
Push your branch
git push origin feature/your-feature-name
Create a pull request on GitHub
Use the provided PR template
Describe your changes clearly
Link any related issues
Wait for review and address feedback
Coding Standards
Crystal Code Style
Follow Crystal's official style guide:
# Good: Use 2 spaces for indentation
class MyClass
def my_method
puts "Hello, World!"
end
end
# Good: Use snake_case for methods and variables
def calculate_total_price
base_price + tax_amount
end
# Good: Use PascalCase for classes and modules
class UserService
module Constants
MAX_RETRIES = 3
end
end
File Organization
# Good: Organize files logically
src/
├── azu_cli/
│ ├── commands/ # Command implementations
│ ├── generators/ # Generator implementations
│ ├── templates/ # ECR templates
│ ├── config.cr # Configuration management
│ └── utils.cr # Utility functions
Documentation
Document all public APIs:
# Good: Document public methods
class UserService
# Creates a new user with the given attributes
#
# @param attributes [Hash(String, String)] User attributes
# @return [User] The created user
# @raise [ValidationError] If attributes are invalid
def self.create_user(attributes : Hash(String, String)) : User
# Implementation
end
end
Testing
Write comprehensive tests:
# spec/services/user_service_spec.cr
describe UserService do
describe ".create_user" do
it "creates a user with valid attributes" do
attributes = {"name" => "John Doe", "email" => "john@example.com"}
user = UserService.create_user(attributes)
user.name.should eq("John Doe")
user.email.should eq("john@example.com")
end
it "raises error with invalid attributes" do
attributes = {"name" => "", "email" => "invalid-email"}
expect_raises(ValidationError) do
UserService.create_user(attributes)
end
end
end
end
Adding New Commands
1. Create Command File
# src/azu_cli/commands/my_command.cr
class Azu::Commands::MyCommand < Azu::Commands::Base
def initialize(@name : String, @options : Hash(String, String))
end
def call
# Command implementation
puts "Executing my command: #{@name}"
end
def self.help : String
"Description of what this command does"
end
end
2. Add Command to Registry
# src/azu_cli/command.cr
module Azu::Commands
# Add your command to the registry
COMMANDS["my-command"] = MyCommand
end
3. Write Tests
# spec/azu_cli/commands/my_command_spec.cr
describe Azu::Commands::MyCommand do
describe "#call" do
it "executes the command successfully" do
command = Azu::Commands::MyCommand.new("test", {} of String => String)
# Test command execution
command.call
# Assert expected behavior
end
end
end
4. Update Documentation
# docs/commands/my-command.md
# My Command
Description of the command and its usage.
## Usage
```bash
azu my-command NAME [OPTIONS]
```
Examples
azu my-command example
## Adding New Generators
### 1. Create Generator Class
```crystal
# src/azu_cli/generators/my_generator.cr
class Azu::Generators::MyGenerator < Azu::Generators::Base
def initialize(@name : String, @options : Hash(String, String))
end
def generate
# Generator implementation
create_file("src/models/#{@name.underscore}.cr", render_model_template)
create_file("spec/models/#{@name.underscore}_spec.cr", render_spec_template)
end
private def render_model_template : String
ECR.render("src/templates/generators/my_generator/model.cr.ecr")
end
private def render_spec_template : String
ECR.render("src/templates/generators/my_generator/model_spec.cr.ecr")
end
end
2. Create Templates
# src/templates/generators/my_generator/model.cr.ecr
class <%= @name.camelcase %> < CQL::Model
table :<%= @name.underscore.pluralize %>
# Add your columns here
# column :name, String
timestamps
end
3. Add Generator to Command
# src/azu_cli/commands/generate.cr
class Azu::Commands::Generate < Azu::Commands::Base
def call
case @subcommand
when "my-generator"
Azu::Generators::MyGenerator.new(@name, @options).generate
# ... other generators
end
end
end
4. Write Tests
# spec/azu_cli/generators/my_generator_spec.cr
describe Azu::Generators::MyGenerator do
describe "#generate" do
it "creates model file" do
generator = Azu::Generators::MyGenerator.new("User", {} of String => String)
generator.generate
File.exists?("src/models/user.cr").should be_true
end
end
end
Adding New Templates
1. Create Template Directory
src/templates/
└── generators/
└── my_generator/
├── model.cr.ecr
├── model_spec.cr.ecr
└── migration.cr.ecr
2. Write Template Files
# src/templates/generators/my_generator/model.cr.ecr
class <%= @name.camelcase %> < CQL::Model
table :<%= @name.underscore.pluralize %>
<% @attributes.each do |attr| %>
column :<%= attr.name %>, <%= attr.type %>
<% end %>
timestamps
# Validations
<% @attributes.select(&.required?).each do |attr| %>
validates :<%= attr.name %>, presence: true
<% end %>
end
3. Test Templates
# spec/templates/my_generator_spec.cr
describe "MyGenerator templates" do
it "renders model template correctly" do
context = {
"name" => "User",
"attributes" => [
{"name" => "name", "type" => "String", "required" => true},
{"name" => "email", "type" => "String", "required" => true}
]
}
result = ECR.render("src/templates/generators/my_generator/model.cr.ecr", context)
result.should contain("class User < CQL::Model")
result.should contain("column :name, String")
result.should contain("validates :name, presence: true")
end
end
Bug Reports
Before Reporting
Check existing issues - Search for similar issues
Reproduce the bug - Create a minimal reproduction
Check documentation - Ensure it's not a configuration issue
Bug Report Template
## Bug Description
Brief description of the bug
## Steps to Reproduce
1. Run command: `azu command --option`
2. Expected: Expected behavior
3. Actual: Actual behavior
## Environment
- Crystal version: `crystal --version`
- Azu CLI version: `azu version`
- OS: macOS/Linux/Windows
- Database: PostgreSQL/MySQL/SQLite
## Additional Information
- Error messages
- Stack traces
- Configuration files
Feature Requests
Before Requesting
Check roadmap - Feature might already be planned
Search issues - Similar feature might be requested
Consider scope - Ensure it fits the project's goals
Feature Request Template
## Feature Description
Clear description of the feature
## Use Case
Why this feature is needed and how it would be used
## Proposed Implementation
Optional: How you think it could be implemented
## Alternatives Considered
Other approaches you've considered
## Additional Context
Any other relevant information
Pull Request Guidelines
PR Template
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Tests added/updated
- [ ] All tests pass
- [ ] Manual testing completed
## Documentation
- [ ] Documentation updated
- [ ] No documentation needed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-review completed
- [ ] Comments added for complex code
- [ ] Corresponding issue linked
Review Process
Automated checks must pass
Code review by maintainers
Documentation review if needed
Final approval before merge
Release Process
Versioning
We follow Semantic Versioning:
Major (X.0.0): Breaking changes
Minor (0.X.0): New features, backward compatible
Patch (0.0.X): Bug fixes, backward compatible
Release Steps
Update version in
shard.yml
Update changelog in
CHANGELOG.md
Create release branch from main
Run full test suite
Create GitHub release
Tag release with version number
Community Guidelines
Code of Conduct
Be respectful to all contributors
Be inclusive and welcoming
Be constructive in feedback
Be patient with newcomers
Communication
GitHub Issues for bug reports and feature requests
GitHub Discussions for questions and ideas
Discord for real-time chat and support
Email for security issues
Getting Help
Resources
Documentation - Comprehensive guides
Examples - Code examples and tutorials
GitHub Issues - Bug reports and discussions
Discord - Community chat
Mentorship
New contributors can:
Start with good first issues - Look for
good first issue
labelAsk for help - Don't hesitate to ask questions
Join discussions - Participate in community conversations
Review others' PRs - Learn by reviewing code
Recognition
Contributors
All contributors are recognized in:
GitHub contributors page
CHANGELOG.md for significant contributions
README.md for maintainers
Release notes for each release
Hall of Fame
Special recognition for:
Major contributors - Significant code contributions
Documentation heroes - Documentation improvements
Community leaders - Community building efforts
Bug hunters - Critical bug reports and fixes
Related Documentation
Development Setup - Detailed setup instructions
Adding New Generators - Generator development guide
Adding New Commands - Command development guide
Testing Guidelines - Testing best practices
Documentation Guidelines - Documentation standards
Last updated