CLI Framework (Topia)
Azu CLI is built on top of Topia, a modern, high-performance task automation framework built with Crystal. Topia provides the foundation for command parsing, task execution, and the overall CLI architecture.
Overview
Topia is a Crystal-powered task automation and build pipeline framework that transforms development workflows. It provides:
Code over Configuration - Write workflows in pure Crystal, no complex config files
High Performance - Built for speed with async operations, caching, and parallelism
Composable - Chain tasks, plugins, and commands like building blocks
Type Safe - Leverage Crystal's compile-time type checking for bulletproof workflows
Developer Friendly - Professional CLI, interactive modes, and comprehensive debugging tools
Core Components
Task System
Topia's task system is the foundation of Azu CLI's command execution:
# Simple command task
Topia.task("build")
.describe("Build the application")
.command("crystal build --release src/main.cr")
# Task with dependencies
Topia.task("test")
.describe("Run tests")
.depends_on("build")
.command("crystal spec")
# Task with file watching
Topia.task("dev")
.describe("Development server with hot reload")
.src("./src/**/*.cr")
.pipe(FileWatcher.new)
.command("crystal run src/main.cr")
Command Execution
Topia provides robust command execution with error handling and output management:
class Azu::Commands::Base
include Topia::Command
def execute_command(command : String, args : Array(String) = [] of String)
Topia::Command.execute(command, args) do |result|
case result
when .success?
Azu::Logger.info("Command executed successfully")
when .failure?
Azu::Logger.error("Command failed: #{result.error}")
raise CommandError.new("Command execution failed")
end
end
end
end
Plugin System
Topia's plugin system allows Azu CLI to extend functionality through composable plugins:
# File processing plugin for code generation
class FileProcessor < Topia::BasePlugin
def run(input, args)
case input
when Array(Topia::InputFile)
announce "Processing #{input.size} files..."
input.map { |file| process_file(file) }
else
error "Expected Array(InputFile), got #{input.class}"
input
end
end
private def process_file(file : Topia::InputFile)
# Process file content
file.contents = file.contents.gsub(/old/, "new")
file
end
end
CLI Architecture
Command Parsing
Topia provides automatic command-line argument parsing:
# Azu CLI command structure
class Azu::CLI
include Topia::CLI
def initialize
@parser = Topia::Parser.new do |parser|
parser.banner = "Azu CLI - Crystal web framework command line tool"
parser.on("new PROJECT_NAME", "Create a new Azu project") do |name|
Azu::Commands::New.new(name).call
end
parser.on("generate TYPE NAME", "Generate code") do |type, name|
Azu::Commands::Generate.new(type, name).call
end
parser.on("serve", "Start development server") do
Azu::Commands::Serve.new.call
end
end
end
def run(args = ARGV)
@parser.parse(args)
end
end
Subcommand Support
Topia enables hierarchical command structures:
# Generate command with subcommands
class Azu::Commands::Generate < Azu::Commands::Base
def call
case @subcommand
when "model"
Azu::Generators::Model.new(@name, @options).generate
when "endpoint"
Azu::Generators::Endpoint.new(@name, @options).generate
when "scaffold"
Azu::Generators::Scaffold.new(@name, @options).generate
else
raise ArgumentError.new("Unknown generator type: #{@subcommand}")
end
end
end
Performance Features
Async Operations
Topia provides non-blocking operations for better performance:
# Async file watching for development server
class Azu::Commands::Serve < Azu::Commands::Base
def call
Topia.task("serve")
.src("./src/**/*.cr")
.pipe(AsyncFileWatcher.new)
.command("crystal run src/main.cr")
.run
end
end
class AsyncFileWatcher < Topia::BasePlugin
def run(input, args)
spawn do
watch_files(input)
end
input
end
private def watch_files(files)
# Non-blocking file watching
files.each do |file|
if file.modified?
rebuild_application
end
end
end
end
Intelligent Caching
Topia provides automatic caching based on input file checksums and command signatures:
# Cached task execution
Topia.task("expensive_build")
.src("./src/**/*.cr")
.pipe(SlowCompiler.new)
.dist("./build/")
.cache(true) # Enable caching
# First run: Full execution
# Subsequent runs: Instant cache hits (if nothing changed)
Parallel Execution
Topia supports concurrent task execution for improved performance:
# Parallel test execution
Topia.task("test")
.parallel(4) # Run 4 tests in parallel
.src("./spec/**/*_spec.cr")
.pipe(TestRunner.new)
.run
Configuration Integration
Environment Variable Support
Topia integrates with Azu CLI's configuration system:
# Configuration-aware task execution
class Azu::Commands::Database < Azu::Commands::Base
def call
config = Azu::Config.current
Topia.task("database_operation")
.env({
"DATABASE_URL" => config.database.url,
"DB_HOST" => config.database.host,
"DB_PORT" => config.database.port.to_s
})
.command("crystal run db/migrate.cr")
.run
end
end
YAML Configuration
Topia supports YAML-based configuration:
# topia.yml
tasks:
build:
command: "crystal build --release src/main.cr"
description: "Build the application"
test:
command: "crystal spec"
description: "Run tests"
depends_on: ["build"]
serve:
command: "crystal run src/main.cr"
description: "Start development server"
watch: ["src/**/*.cr"]
Debugging and Monitoring
Enhanced Debugging
Topia provides comprehensive debugging capabilities:
# Debug mode with detailed logging
Topia.debug = true
# CLI debugging options
./azu_cli -d task_name # Debug mode with detailed logging
./azu_cli --verbose --stats task_name # Verbose output with performance stats
./azu_cli --profile task_name # Performance profiling
./azu_cli --dependencies task_name # Analyze task dependencies
./azu_cli --where task_name # Find task source location
./azu_cli --dry-run task_name # Preview execution without running
Performance Metrics
Topia provides detailed performance insights:
# Performance profiling
Topia.task("monitored")
.describe("Task with rich monitoring")
.command("long_running_process")
.profile(true) # Enable performance profiling
# Automatically tracks:
# - Execution time
# - Success/failure rates
# - Cache hits
# - Memory usage
# - CPU utilization
Lifecycle Hooks
Plugin Lifecycle
Topia provides lifecycle hooks for plugins:
class Azu::Generators::Base < Topia::BasePlugin
def on(event : String)
case event
when "pre_run"
setup_environment
when "after_run"
cleanup_resources
when "error"
handle_error
end
end
private def setup_environment
Azu::Logger.info("Setting up generator environment")
# Initialize generator state
end
private def cleanup_resources
Azu::Logger.info("Cleaning up generator resources")
# Clean up temporary files
end
private def handle_error
Azu::Logger.error("Generator encountered an error")
# Error recovery logic
end
end
Integration with Azu CLI
Command Structure
Azu CLI uses Topia's command system for all CLI operations:
# Main CLI entry point
class Azu::CLI
include Topia::CLI
def initialize
setup_commands
setup_middleware
end
private def setup_commands
# Project management commands
register_command("new", Azu::Commands::New)
register_command("init", Azu::Commands::Init)
# Code generation commands
register_command("generate", Azu::Commands::Generate)
# Database commands
register_command("db", Azu::Commands::Database)
# Development commands
register_command("serve", Azu::Commands::Serve)
register_command("dev", Azu::Commands::Dev)
end
private def setup_middleware
# Add middleware for logging, error handling, etc.
use(Azu::Middleware::Logging)
use(Azu::Middleware::ErrorHandler)
end
end
Error Handling
Topia provides robust error handling for Azu CLI:
class Azu::Middleware::ErrorHandler < Topia::Middleware
def call(context : Topia::Context) : Topia::Context
begin
call_next(context)
rescue ex : Azu::Error
Azu::Logger.error("Azu CLI error: #{ex.message}")
context.exit_code = 1
rescue ex : Exception
Azu::Logger.error("Unexpected error: #{ex.message}")
Azu::Logger.debug(ex.backtrace.join("\n"))
context.exit_code = 1
end
context
end
end
Performance Benefits
Before Topia Integration
Build time: 45s
CPU usage: 15% (spinner)
Memory: Growing over time
Cache hits: 0%
After Topia Integration
Build time: 12s (with parallelism + caching)
CPU usage: <1% (async spinner)
Memory: Stable with cleanup
Cache hits: 85%+
Real-World Results
Medium project (50 files): 40s → 8s (5x faster)
Large project (200+ files): 3min → 45s (4x faster)
CI pipeline: 8min → 2min (4x faster)
Best Practices
Task Design
Keep tasks focused: Each task should have a single responsibility
Use dependencies: Leverage Topia's dependency system for proper ordering
Enable caching: Use caching for expensive operations
Profile performance: Use Topia's profiling tools to identify bottlenecks
Plugin Development
Follow the interface: Implement
Topia::BasePlugin
correctlyHandle errors gracefully: Provide meaningful error messages
Use lifecycle hooks: Implement proper setup and cleanup
Document your plugin: Provide clear documentation for users
Configuration
Use environment variables: For sensitive configuration
Provide defaults: Always provide sensible default values
Validate configuration: Use Topia's validation features
Keep it simple: Avoid overly complex configuration structures
Related Documentation
Topia Repository - Official Topia documentation
Architecture Overview - Overall Azu CLI architecture
Generator System - Code generation architecture
Configuration System - Configuration management
Plugin System - Plugin development guide
Last updated